1

We are upgrading our existing Identity Server solution form ID4 to Duende Identity Server with .net 6.0

We also use RSK's SAML plugin for Duende Identity Server.

Right now I'm trying to create our own SAML Identity Provider store, instead of the In memory store provided.

The issue I'm facing is the SAML 2p Authentication Handler is throwing a null reference exception when calling the "HandleChallengeAsync(AuthenticationProperties properties)" method. But it doesn't tell me what the null reference is in relation to.

While stepping through the code using the InMemory implementation everything works. I'm attaching the code to create the SamlDynamicIdentityProvider as well as the exception.

Saml IdP Store

public class SamlIdPStore : SamlDynamicIdentityProvider, IIdentityProviderStore
    {
        private readonly ILogger _logger;
        private readonly IEnumerable<IdentityProvider> _providers;

        /*
        public SamlIdPStore(ILogger logger)
        {
            _logger = logger;
        }
       */
        
        public SamlIdPStore(IEnumerable<IdentityProvider> providers, ILogger logger)
        {
            _logger = logger;
            _providers = providers;
        }
        

        public async Task<IEnumerable<IdentityProviderName>> GetAllSchemeNamesAsync()
        {
            var samlIdPConfigs = await SamlIdPConfigurationManagerFactory.GetAllSamlIdPConfigs();

            var identityProviderNames = new List<IdentityProviderName>();

            foreach (var samlIdPConfig in samlIdPConfigs)
            {
                var identityProviderName = new IdentityProviderName()
                {
                    DisplayName = samlIdPConfig.DisplayName,
                    Enabled = true,
                    Scheme = samlIdPConfig.AuthenticationScheme
                };

                identityProviderNames.Add(identityProviderName);
            }

            return identityProviderNames;

        }

        public async Task<IdentityProvider> GetBySchemeAsync(string scheme)
        {
            var samlIdentityProvider = await SamlIdPConfigurationManagerFactory.GetSamlIdPConfig(scheme);

            var identityProvider = new IdentityProvider("saml2p")
            {
                Scheme = scheme,
                DisplayName = samlIdentityProvider.DisplayName,
                Enabled = true
               
            };
            
            var samlDynamicProvider = Saml2p.CreateDynamicProviderFromConfig(samlIdentityProvider, _logger);

            identityProvider.Properties = samlDynamicProvider.Properties;
            return identityProvider;
        }
    }

The exception We're getting is -

System.NullReferenceException: Object reference not set to an instance of an object.
   at Rsk.AspNetCore.Authentication.Saml2p.Saml2pAuthenticationHandler.HandleChallengeAsync(AuthenticationProperties properties)
   at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.ChallengeAsync(AuthenticationProperties properties)
   at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties)
   at Microsoft.AspNetCore.Mvc.ChallengeResult.ExecuteResultAsync(ActionContext context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|22_0(ResourceInvoker invoker, IActionResult result)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Rsk.Saml.Hosting.IdentityServerSamlMiddleware.Invoke(HttpContext context, ISamlEndpointRouter router, ISamlEventService eventService, SamlIdpOptions options)
   at Duende.IdentityServer.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IIssuerNameService issuerNameService, IBackChannelLogoutService backChannelLogoutService) in /_/src/IdentityServer/Hosting/IdentityServerMiddleware.cs:line 102
   at Duende.IdentityServer.Hosting.MutualTlsEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes) in /_/src/IdentityServer/Hosting/MutualTlsEndpointMiddleware.cs:line 94
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Duende.IdentityServer.Hosting.DynamicProviders.DynamicSchemeAuthenticationMiddleware.Invoke(HttpContext context) in /_/src/IdentityServer/Hosting/DynamicProviders/DynamicSchemes/DynamicSchemeAuthenticationMiddleware.cs:line 47
   at Duende.IdentityServer.Hosting.BaseUrlMiddleware.Invoke(HttpContext context) in /_/src/IdentityServer/Hosting/BaseUrlMiddleware.cs:line 27
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

In Memory Implementation

public static IIdentityServerBuilder AddSaml2pIdPFromConfiguration(this IIdentityServerBuilder builder,
                                                                       ISamlIdpConfigurationManager idpConfigManager,
                                                                       ILogger logger)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            //only create the SAML IDP if we have a valid key in the config file
            if (string.IsNullOrWhiteSpace(ConfigSettings.SamlLicenseKey))
            {
                return builder;
            }

            try
            {
                builder.AddSamlDynamicProvider(options =>
                {
                    options.Licensee = ConfigSettings.SamlLicensee;
                    options.LicenseKey = ConfigSettings.SamlLicenseKey;
                });

                // add saml2p as defined by each configuration in the saml configuation file
                var samlDynamicIdentityProviders = new List<SamlDynamicIdentityProvider>();
                foreach (var samlIdpConfig in idpConfigManager.GetIdpConfigurations())
                {
                    var samlDynamicIdentityProvider = CreateDynamicProviderFromConfig(samlIdpConfig,logger);
                    samlDynamicIdentityProviders.Add(samlDynamicIdentityProvider);
                }

                builder.AddInMemoryIdentityProviders(samlDynamicIdentityProviders);
                //builder.AddIdentityProviderStore<SamlIdPStore>();

            }
            catch (Exception ex)
            {
                logger?.LogError(ex, $"Error adding SAML2P idp configuration");
            }


            return builder;
        }

Any help would be greatly appreciated.

Thanks, Gautham

1 Answers1

0

After talking to RSK's support line, the answer to my problem was fairly simple. I should not have been extending the SamlDynamicIdentityProvider in my class, just implementing the IIdentityProviderStore.

Class Definition is now - public class SamlIdPStore : IIdentityProviderStore

and everything works.

  • Hi, how did you migrate rsk.saml database schema? I am also trying to achieve same thing, I have upgraded IS4 to DuendeIdentity server V6 and for oidc protocol everything is working fine, but when I upgraded rsk libraries then I am getting couple of database screma related errors for SAML as below: SqlException: Invalid column name 'ArtifactDeliveryBindingType'. Invalid column name 'NameIdentifierFormat'. Invalid column name 'RequireSignedArtifactResolveRequests'. Invalid column name 'RequireSignedArtifactResponses'. – Jaydeep Suryawanshi Sep 27 '22 at 13:27