Tuesday, August 23, 2011

ADFS 2.0 – MSIS7012/MSIS3127 when accepting claims from a custom claims provider

The scenario is as follows. You provide a custom claims provider for ADFS2.0 so that when user logs-in to the application, the ADFS should offer a list of possible authentication providers. When the custom provider is selected, ADFS redirects the browser to it. Then, ADFS should accept incoming claims and pass them through to the relying party application.

There are plenty of useful scenarios when this could be handy and people blog about it, for example take a look at this article. In one of our projects we go this route, trying to provide a custom STS to support ADFS with various login options (username/password, email/password, uniqueid/password, name.surname/password etc).

What people seem to have problems with is the number of different issues which arise when the ADFS is up to accept and transform incoming claims. One of such common issues show as MSIS7012/MSIS3127 in the event log:

Microsoft.IdentityServer.Web.RequestFailedException: MSIS7012: Wystąpił błąd podczas przetwarzania żądania. Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem. ---> System.ServiceModel.FaultException: MSIS3127: Określone żądanie nie powiodło się.
   w Microsoft.IdentityServer.Protocols.WSTrust.WSTrustClientManager.Issue(Message request, WCFResponseData responseData)
   w Microsoft.IdentityServer.Protocols.WSTrust.WSTrustClient.Issue(RequestSecurityToken rst, WCFResponseData responseData)
   w Microsoft.IdentityServer.Web.FederationPassiveAuthentication.SubmitRequest(MSISRequestSecurityToken request)
   --- Koniec śladu stosu wyjątków wewnętrznych ---
   w Microsoft.IdentityServer.Web.FederationPassiveAuthentication.SubmitRequest(MSISRequestSecurityToken request)
   w Microsoft.IdentityServer.Web.FederationPassiveAuthentication.RequestBearerToken(MSISSignInRequestMessage signInRequest, SecurityTokenElement onBehalfOf, SecurityToken primaryAuthToken, String desiredTokenType, Uri& replyTo)
   w Microsoft.IdentityServer.Web.FederationPassiveAuthentication.RequestBearerToken(MSISSignInRequestMessage signInRequest, SecurityTokenElement onBehalfOf, SecurityToken primaryAuthToken, String desiredTokenType, MSISSession& session)
   w Microsoft.IdentityServer.Web.FederationPassiveAuthentication.BuildSignInResponseCoreWithSerializedToken(String signOnToken, WSFederationMessage incomingMessage)
   w Microsoft.IdentityServer.Web.FederationPassiveAuthentication.BuildSignInResponseForProtocolResponse(FederationPassiveContext federationPassiveContext)
   w Microsoft.IdentityServer.Web.FederationPassiveAuthentication.BuildSignInResponse(SecurityToken securityToken)

System.ServiceModel.FaultException: MSIS3127: Określone żądanie nie powiodło się.
   w Microsoft.IdentityServer.Protocols.WSTrust.WSTrustClientManager.Issue(Message request, WCFResponseData responseData)
   w Microsoft.IdentityServer.Protocols.WSTrust.WSTrustClient.Issue(RequestSecurityToken rst, WCFResponseData responseData)
   w Microsoft.IdentityServer.Web.FederationPassiveAuthentication.SubmitRequest(MSISRequestSecurityToken request)

If you take a look at the accompanying event log entry you will learn that:

A token was received from a claims provider identified by the key somekeyhere, but the token could not be validated because the key does not identify any known claims provider trust. Key: somekeyhere. This request failed. User Action If this key represents the certificate thumbprint of a claims provider trust, verify that it matches the signing certificate of the claims provider trust in the AD FS configuration database.

It took like 3 hours to finally find the problem. And the problem is the mismatch between the issuer name provided in the provider’s metadata and the issuer name provided in the ADFS configuration.

First one of these two names is set programmatically somewhere in the configuration of the claims provider, in the SecurityTokenServiceConfiguration class:

public class CustomSecurityTokenServiceConfiguration : SecurityTokenServiceConfiguration
{
    ...
 
    /// <summary>
    /// CustomSecurityTokenServiceConfiguration constructor.
    /// </summary>
    public CustomSecurityTokenServiceConfiguration()
        : base( "ADFS Stub",
                new X509SigningCredentials( the certificate here )
               )
    {
        this.SecurityTokenService = typeof( CustomSecurityTokenService );
    }
}

Note the “ADFS Stub” here – the name is invisible in an explicit form in the metadata because it’s base64’ed somewhere in the certificate description.

The second one of these two names is visible in the explicit form in the ADFS claims trust configuration:

Note that the value of the “Claims provider identifier” attribute (the lower textbox) matches the name provided as the issuer name in the C# code snippet above. If the two do not match you get the exception in the ADFS log.

2 comments:

Henrik Voldby said...

Hello Wiktor

Thank you for you blog post.

We experience the error you describe, although the ADFS claims provider identifier matches the entityID in the metadata from the custom STS. To top this off, the lcaims provider identifier also matches the issuername received in the SAML tokens from the STS.

Do you by chance have any supplementing input, which may resolve the issue?

Any help is greatly appreciated.

Best Regards

Henrik Voldby

Wiktor Zychla said...

Henrik,

the other probable cause is that your ADFS has wrong signing certificate of your custom STS - there's a match between identifiers but certificate's thumbprint does not match. The best way to maintain the correspondence is to publish the dynamic metadata on your custom STS and update the federation on the ADFS, following my tutorial http://netpl.blogspot.com/2011/09/quest-for-customizing-adfs-sign-in-web_12.html. I hope this helps.

Regards,
Wiktor