Sustainsys / Saml2

Saml2 Authentication services for ASP.NET
Other
957 stars 602 forks source link

Best practice for handling saml response status codes #791

Open MartijnKooij opened 7 years ago

MartijnKooij commented 7 years ago

The Dutch IdP requires you to differentiate between a canceled login and all other errors in the login processs. The problem is, they return a response status with an AuthnFailed second lever status to indicate the user has canceled the login. The authservices framework appears to capture all exceptions in the middleware and only exposes a general "error = acces_denied" to the outside world. I think this is correct behaviour, you probably don't want to show too many internal details. But given tihs framework and the IdP's reqiurement, how would I differentiate between true exceptions and their cancel exception?

Any help is greatly appreciated.

FYI AuthnFailed = "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed" We use authservices to connect to the IdP using IdentityServer3

AndersAbel commented 6 years ago

I think that this could be solved by providing a notification that is called when the status is received (both if it is success and if it is a failure) and provide the status information as a param. Then you can run your own code and handle it the way you want. Would that work?

keimpema commented 6 years ago

Found this after I created issue https://github.com/Sustainsys/Saml2/issues/864 . Looks like the same problem. Tried a work around using GetBinding and MessageUnbound notifications to no avail.

The proposed solution could work if we had access to all data added to the request bij Identityserver (StoredRequestState?) and we could change the redirect url I think.

@MartijnKooij Did you get this to work somehow? Looks like we are both trying to make IdentityServer work with DigiD. Would you like to share experiences?

MartijnKooij commented 6 years ago

@keimpema I did not yet have/take the time to work on a decent solution for this issue, nor to look at the suggested route by Anders. We have currently have 2 projects connecting to DigiD, one of them had to keep on using the ASelect protocol. The other one, the one for which I posted this issue, was on a tight deadline. Because of that deadline the team members chose to implement a hacky hard-coded fix to make it work. You can find the pull request containing that workaround on my fork. We will be fixing that, and thus working on this issue, but I cannot yet tell when that will be...

keimpema commented 6 years ago

I made it work (sticked to the work-around). See https://github.com/Sustainsys/Saml2/issues/864.

jbreuer commented 6 years ago

Hi @MartijnKooij. Do you have an example how Sustainsys.Saml2 can be used for DigiD?

MartijnKooij commented 6 years ago

Hi @jbreuer , except for the issue mentioned here (which handles an edge case required by logius) using this framework to authenticate using DigiD is "just" a matter of configuring it correctly. Just follow the sample of your choice (we are currently using it in an IdentityServer3 implemention for which there is a sample available), and provide all of your own and Logius's config values. There's nothing special about the Logius SAML implementation, it is "just" SAML :)

jbreuer commented 6 years ago

Thanks for the feedback @MartijnKooij. Is there a reason you are using IdentityServer3? Currently I want to use SAML for authentication and OAuth for authorization. I'm using this example as a basis: http://blog.scottlogic.com/2015/11/19/oauth2-with-saml2.html. However it's probably better to replace parts of that example with this library.

You mention it's just a matter of configuring it correctly. Do you have an example of this configuration? I'm pretty new to DigiD and SAML so an example could really help me a lot.

MartijnKooij commented 6 years ago
  1. We use IdS 3 because at the time the .Net core version of this SAML2 framework was not ready yet.
  2. The blog post you mention uses a different SAML library of which I have no practical experience. But again, SAML is "just" SAML, so any framework should be able to get you going.
  3. I forgot that for DigiD there's 1 PR still pending here to make it work. Client certificate for artifact resolve: https://github.com/Sustainsys/Saml2/pull/716
  4. Config is explained here: https://github.com/Sustainsys/Saml2/blob/master/docs/Configuration.md and I would advise you to just take the pain of learning it yourself. But to help get you started the config we use is listed below. Please note that it does of course contain placeholders instead of values, and that it is based on the framework when it was still named "Kentor IT AuthServices". I think Anders only renamed the root node, but I haven't upgraded yet.
  <kentor.authServices 
                       entityId="{{IdentityServer.DigiDEntityId}}" 
                       authenticateRequestSigningBehavior="Always" 
                       returnUrl="https://{{IdentityServer.HostName}}/core"                       
                       outboundSigningAlgorithm="SHA1" 
                       minIncomingSigningAlgorithm="SHA1">
    <identityProviders>
      <add entityId="{{IdentityServer.DigiDEntityId}}"
           signOnUrl="{{IdentityServer.DigiDServerUrl}}/saml/idp/request_authentication"
           logoutUrl="{{IdentityServer.DigiDServerUrl}}/saml/idp/request_logout?spentityid={{IdentityServer.DigiDEntityId}}"
           binding="HttpRedirect"
           wantAuthnRequestsSigned="true"
           allowUnsolicitedAuthnResponse="false">
        <signingCertificate storeName="My" storeLocation="LocalMachine" findValue="{{IdentityServer.CertificateIssuer}}" x509FindType="FindBySubjectName" />
        <artifactResolutionServices>
          <add location="{{IdentityServer.DigiDWasServerUrl}}/saml/idp/resolve_artifact?spentityid={{IdentityServer.DigiDEntityId}}" />
        </artifactResolutionServices>        
      </add>
      <add entityId="{{IdentityServer.DigiDWasServerUrl}}/saml/idp/metadata"
           metadataLocation="{{IdentityServer.DigiDWasServerUrl}}/saml/idp/metadata"
           wantAuthnRequestsSigned="true" 
           allowUnsolicitedAuthnResponse="false">
        <signingCertificate storeName="My" storeLocation="LocalMachine" findValue="{{IdentityServer.DigiDWasCertificateIssuer}}" x509FindType="FindBySubjectName" />
      </add>
    </identityProviders>
    <metadata wantAssertionsSigned="true" />
    <requestedAuthnContext classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" comparison="Minimum" />
    <serviceCertificates>
      <add storeName="My" storeLocation="LocalMachine" findValue="{{IdentityServer.CertificateIssuer}}" x509FindType="FindBySubjectName" use="Both" status="Current" />
    </serviceCertificates>
    <artifactResolutionTlsCertificate storeName="My" storeLocation="LocalMachine" findValue="{{IdentityServer.CertificateIssuer}}" x509FindType="FindBySubjectName" />
  </kentor.authServices>
jbreuer commented 6 years ago

Thanks for the config example @MartijnKooij. This is great for getting started.

jbreuer commented 6 years ago

Hi @MartijnKooij. Since you are using IdenityServer as the IdentityProvider do you use this paid module? https://www.identityserver.com/products/#SAML2P

I like the https://github.com/Sustainsys/Saml2/tree/master/Samples/SampleIdentityServer3 example. There Sustainsys.Saml2 is the service provider, but it adds itself as an identity provider to IdenityServer.

I'm still trying to use the scenario from this blogpost: http://blog.scottlogic.com/2015/11/19/oauth2-with-saml2.html. Is it possible with the SampleIdentityServer3 for Sustainsys.Saml2 to do this? So we use Sustainsys.Saml2 as the service provider and after validation connect to IdentityServer. Than IdentityServer gives us the OAuth token we can use.

jbreuer commented 6 years ago

Hi @MartijnKooij. Sorry this is off-topic, but I don't know where else to post this. I've succesfully setup DigiD with Identity Server 3. The only thing I haven't done yet is re-authenticate to prevent a session timeout. See chapter 4.3 of this document: https://www.logius.nl/fileadmin/logius/ns/diensten/digid/koppelvlakspecificaties/Koppelvlakspecificatie_SAML_DigiD4_v3_3.pdf

Have you implemented this feature with Sustainsys.Saml2 and Identity Server 3?

MartijnKooij commented 6 years ago

Sorry, no concrete experience with re-authenticating. Stack overflow would be a better place for general questions not related to the SAML2 framework. IdentityServer refresh tokens? http://docs.identityserver.io/en/release/topics/refresh_tokens.html

jbreuer commented 6 years ago

Thanks I've posted a question on SO: https://stackoverflow.com/questions/51746171/reauthentication-with-identity-server-3-and-saml

AndersAbel commented 6 years ago

Doing a bit of issue tracker cleanup. What is the status of this? If I understand it correctly, the remaining issue here is about getting IdSrv3 to initiate a reauthentication. Which is obviously outside the scope of this project. Can this issue be closed then?

AndersAbel commented 6 years ago

Reading my own comments above, this is listed as an enhancement - to add a notification for status code reception. So I'll keep it open.

jbluemink commented 2 years ago

Instead of use the <artifactResolutionTlsCertificate .../> from the DigiD example from @MartijnKooij In the final version there is use="TlsClient" which I currently can not found documentation https://saml2.sustainsys.com/en/stable/config-elements/service-certificates.html

But for the Dutch DigiD you need this for Successfully sending Artifact Soap Message.

<serviceCertificates>
      <add storeName="My" storeLocation="CurrentUser" findValue="{{IdentityServer.CertificateIssuer}}" x509FindType="FindBySubjectName" use="TlsClient" status="Current" />
</serviceCertificates>
jbreuer commented 2 years ago

Hi @jbluemink. This example is for an older version, but it might help:

var optionsIdpDigid = new Saml2AuthenticationOptions(false)

var certDigid = GetThumbCertificate(ConfigSettings.Saml2Certificate);

optionsIdpDigid.SPOptions.ServiceCertificates.Add(new ServiceCertificate { Use = CertificateUse.Signing, Certificate = certDigid });
optionsIdpDigid.SPOptions.ServiceCertificates.Add(new ServiceCertificate { Use = CertificateUse.TlsClient, Certificate = certDigid });
optionsIdpDigid.SPOptions.AuthenticateRequestSigningBehavior = SigningBehavior.Always;