5

I am having an issue when using ITfoxtec for ASP.NET Core 3.0.
As context I am trying to establish a connection between a webapplication and a third-party login service. To encapsulate some of the possibilities beforehand, the third-party has access to our metadata-url and configured their services for our webapplication.

Desired user workflow:

  • User enters the webapplication;
  • User clicks a button which redirects the user to the login service;
  • User logs in on the service and redirects back to the given returnURL;
  • Afterwards the webapplication determines permission based on the provided sso-cookie.

Steps taken so far:

  • Added Saml2 section in appsettings.json containing our metadata.xml and issuer. The issuer name equals the given EntityID provided within the metadata.xml. It is made anonymous in the given context, like so:
"Saml2": {
    "IdPMetadata": "wwwroot/SAML/Metadata.xml",
    "Issuer": "myIssuerName",
    "SignatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
    "CertificateValidationMode": "ChainTrust",
    "RevocationMode": "NoCheck",
    "SigningCertificateFile": "\\licenses\\certificate.pfx",
    "SigningCertificatePassword": "password1"
}, 
  • Added Saml2Configuration in startup.cs;
    services
        .Configure<Saml2Configuration>(Configuration.GetSection("Saml2"))
        .Configure<Saml2Configuration>(configuration =>
        {
            configuration.SigningCertificate = CertificateUtil.Load(
                 $"{Environment.WebRootPath}{Configuration["Saml2:SigningCertificateFile"]}",
                 Configuration["Saml2:SigningCertificatePassword"]);
            configuration.AllowedAudienceUris.Add(configuration.Issuer);

            var entityDescriptor = new EntityDescriptor();
                entityDescriptor.ReadIdPSsoDescriptorFromFile(Configuration["Saml2:IdpMetadata"]);

            if (entityDescriptor.IdPSsoDescriptor == null) throw new Exception("Failed to read the metadata.");

            configuration.SignAuthnRequest = true;
            configuration.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices
               .Where(ed => ed.Binding.ToString() == "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
               .First().Location;
            configuration.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates);
        }); 
  • Here comes the tricky part; By default the sso initiation does a request with a RedirectBinding which does therefore send a GET request towards the sso service. However, the service I am trying to approach expects a SAMLRequest as a POST request. So I have changed the code by initiating with PostBinding request and afterwards directly submit the form, like so:
    public IActionResult Initiate([FromQuery(Name = "returnUrl")] string returnUrl = "")
    {
        var binding = new Saml2PostBinding();
            binding.SetRelayStateQuery(new Dictionary<string, string> { { "ReturnUrl", returnUrl } });
            binding.Bind(new Saml2AuthnRequest(_saml2configuration)
            {
                ForceAuthn = false,
                IsPassive = false,
                NameIdPolicy = new NameIdPolicy() { AllowCreate = true },
                AssertionConsumerServiceUrl = new Uri("https://localhost:44366/api/Authentication/Process"),
            });

        return binding.ToActionResult();
    } 

Issue:
However, after sending the base64 encoded AuthnRequest as SAML Request, I am receiving a 403 Forbidden from the third-party login. At this stage I am not certain whether is the identity provider not being configured properly or my request lacking something. What am I doing wrong?

Below is the (anonymously made) request headers.
Assume that the SAMLRequest is provided in formdata as base64 encoded.

    :authority: myEntityDescriptorName
    :method: POST
    :path: mySsoURL
    :scheme: https
    accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
    accept-encoding: gzip, deflate, br
    accept-language: nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7
    cache-control: no-cache
    content-length: 3582
    content-type: application/x-www-form-urlencoded
    cookie: JSESSIONID=3D5FE88D55674C2F1E3646E6D8A0FFBE
    origin: https://localhost:44366
    pragma: no-cache
    referer: https://localhost:44366/
    sec-fetch-mode: navigate
    sec-fetch-site: cross-site
    sec-fetch-user: ?1
    upgrade-insecure-requests: 1
    user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36

1 Answers1

1

It is correct to change the Authn Request to a Post binding if that is required.

Your application is a Service Provider (also called Relying Party) which needs to be configured at the Identity Provider with a unique Issuer name.

I think the problem is that the Issuer name you have configured ("Issuer": "myIssuerName") is incorrect. The issuer name should be your Service Providers issuer name, not the Identity Provider Issuer name from the metadata.xml.

Cameron Tinker
  • 9,634
  • 10
  • 46
  • 85
Anders Revsgaard
  • 3,636
  • 1
  • 9
  • 25
  • 1
    Using Post Binding does indeed work, however whenever I initiate my call I seem to be getting a form whereas the button is hidden by – Lucas van Liere Nov 21 '19 at 13:30
  • 1
    The Post Binding form are auto submitted by JavaScript unless JavaScript is disabled. Therefore, I think JavaScript is disabled in the client browser. You can try to test with another browser or maybe you are running the browser on a server. – Anders Revsgaard Nov 22 '19 at 09:18
  • 1
    I doubt it is client browser related. If check my Chrome or Mozilla browser options, I can see that they are enabled within the options in both browsers. Could it be that .NET Core uses SpaStaticFiles for hosting the Angular files and is therefore seen as a browser running on the server? Regardless, how is ITfoxtec.Identity.SAML checking if JavaScript is enabled so I could prevent this from happening? – Lucas van Liere Nov 22 '19 at 09:45
  • 1
    I have not heard of others having a problem with auto submit, I'm afraid that I do not have a good answer. If JavaScript is disabled an button should appear, otherwise it is hidden by the – Anders Revsgaard Nov 22 '19 at 13:14
  • 1
    As a workaround I have added the form prematurely on the front-end and request the Saml2PostBinding on page load. Once the request is finished, I put the SAMLRequest value(s) in my form which I submit whenever the user clicks the login button. It works, doesn't luckily seem to hacky and provides me a freshly signed signature for each attempt. This does however kinda destroy the purpose from auto submitting. – Lucas van Liere Nov 22 '19 at 13:44
  • 1
    That is a secure workaround. However, I am surprised that auto submit does not work. When the IdP return the SAML 2.0 Authn Response to your application it is likely also by form submit. Is it possible for you to se what is different compared to how auto submit is done in ITfoxtec Identity SAML 2.0. – Anders Revsgaard Nov 25 '19 at 08:06
  • 1
    The only difference I could find is the Accept-header whereas the IdP does support text/html and mine does not. I have tried to manipulate that specific header before, but for some reason it was neglected for any call I tried to do with ITfoxtec Identity. Perhaps the auto submit is unable to tell if JavaScript is enabled whenever the Response Type is not allowed by the Request Accept headers? – Lucas van Liere Nov 25 '19 at 08:30
  • 1
    Thanks for investigating. I'm afraid I still do not know why it fails. The ContentType = "text/html" is actually specified by the package. I'm glad you found a workaround through. – Anders Revsgaard Nov 26 '19 at 20:01