Search code examples
c#asp.net-coresaml-2.0itfoxtec-identity-saml2

Why is my service provider web app throwing this error when I click the login button?


I am building a .Net Core Web App that will be a service provider and use SSO to sign in users via an Identity Provider.

When I load the web app, and click Login...I get errors....

I have the following in my appsettings.json file:

  "Saml2": {
    "zau_idpMetadata": "https://saml.zau.edu/zau_idp/shibboleth ",
    "Issuer": "UniversityComms",
    "SingleSignOnDestination": "https://saml.zau.edu/zau_idp/profile/SSO",
    "SingleLogoutDestination": "https://saml.zau.edu/zau_idp/profile/Logout",
    "CertificateValidationMode": "None",
    //"CertificateValidationMode": "ChainTrust",
    "RevocationMode": "NoCheck"
  }

You can see that I have SingleLogoutDestination defined.

And this is in my program.cs file:

builder.Services.Configure<Saml2Configuration>(saml2Configuration =>
{
    saml2Configuration.AllowedAudienceUris.Add(saml2Configuration.Issuer);

    var entityDescriptor = new EntityDescriptor();
    //entityDescriptor.ReadIdPSsoDescriptorFromUrlAsync(httpClientFactory, new Uri(Configuration["Saml2:IdPMetadata"])).GetAwaiter().GetResult();
    entityDescriptor.ReadIdPSsoDescriptorFromUrl(new Uri(configuration["Saml2:IdPMetadata"]));
    if (entityDescriptor.IdPSsoDescriptor != null)
    {
        saml2Configuration.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;
        saml2Configuration.SingleLogoutDestination = entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.First().Location;
        saml2Configuration.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates);
    }
    else
    {
        throw new Exception("IdPSsoDescriptor not loaded from metadata.");
    }
});

builder.Services.AddSaml2();

var app = builder.Build();

However, when I start the app, and try to login via SSO, I get this error:

System.InvalidOperationException: 'Sequence contains no elements'

Here is the full error:

  InvalidOperationException: Sequence contains no elements

    System.Linq.ThrowHelper.ThrowNoElementsException()
    System.Linq.Enumerable.First<TSource>(IEnumerable<TSource> source)
    Program+<>c__DisplayClass0_0.<<Main>$>b__0(Saml2Configuration saml2Configuration) in Program.cs

        var entityDescriptor = new EntityDescriptor();
        //entityDescriptor.ReadIdPSsoDescriptorFromUrlAsync(httpClientFactory, new Uri(Configuration["Saml2:IdPMetadata"])).GetAwaiter().GetResult();
        entityDescriptor.ReadIdPSsoDescriptorFromUrl(new Uri(configuration["Saml2:IdPMetadata"]));
        if (entityDescriptor.IdPSsoDescriptor != null)
        {
            saml2Configuration.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;

            saml2Configuration.SingleLogoutDestination = entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.First().Location;

            saml2Configuration.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates);
        }
        else
        {
            throw new Exception("IdPSsoDescriptor not loaded from metadata.");
        }

Microsoft.Extensions.Options.OptionsFactory<TOptions>.Create(string name)
Microsoft.Extensions.Options.UnnamedOptionsManager<TOptions>.get_Value()
UniversityComms.Controllers.AuthController..ctor(IOptions<Saml2Configuration> configAccessor) in AuthController.cs

        {
            const string relayStateReturnUrl = "ReturnUrl";
            private readonly Saml2Configuration config;
            public AuthController(IOptions<Saml2Configuration> configAccessor)
            {

                config = configAccessor.Value;

            }
            [Route("Login")]
            public IActionResult Login(string? returnUrl = null)
            {
                var binding = new Saml2RedirectBinding();

lambda_method22(Closure , IServiceProvider , object[] )

Solution

  • The code loads the SingleSignOnDestination and SingleLogoutDestination from the IdP metadata endpoint zau_idpMetadata. The IdP metadata probably do not contain at lease one SingleLogoutServices and therefore it fails on .First().

    You can comment out the single logout line and load it from config.