On my MVC site, I redirect to an ADFS login page if I detect an ADFS account is being used. After the user enters their ADFS credentials, the ADFS site posts a WsFederationMessage back to my site. How can I validate the ADFS token that is presented to my site as part of this WsFederationMessage?

Inside of an AuthenticationHandler middleware class, I have the following relevant code which calls the ValidateToken method:

IFormCollection form = await Request.ReadFormAsync();

WsFederationMessage wsFederationMessage = new WsFederationMessage(form);

if (!wsFederationMessage.IsSignInMessage)
{
    Request.Body.Seek(0, SeekOrigin.Begin);
    return null;
}

var token = wsFederationMessage.GetToken();

if (wsFederationMessage.Wresult != null && Options.SecurityTokenHandlers.CanReadToken(token))
{
    SecurityToken validatedToken;
    ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(token, Options.TokenValidationParameters, out validatedToken);
    ...
}

I got this error when I tried to call ValidateToken:

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.IdentityModel.SignatureVerificationFailedException: ID4037: The key needed to verify the signature could not be resolved from the following security key identifier 'SecurityKeyIdentifier (
IsReadOnly = False, Count = 1, Clause[0] = X509RawDataKeyIdentifierClause(RawData = [Removed by Author]. Ensure that the SecurityTokenResolver is populated with the required key.

Searching for a resolution, I found this article, so I decoded the X509Certificate presented within the token string object in my code above using this site's OpenSSL-based decoder, as it was PEM-encoded within the <X509Certificate></X509Certificate> XAML tag of the returned token string. Indeed, it was the signing certificate as the resolution article says it should be. So I went on my ADFS server, exported the signing certificate as a public certificate and installed it on my website's Trusted Root Certificate Authorities. The link also mentioned that I have to:

Import the certificate to the Signature tab of the RP Trust

So I added the signing certificate to the Signature tab of my Relying Party Trust on my ADFS server where I have a trust rule for my machine's identifier. After all of this, it still didn't work. A little bit of background though, my website is running locally on my machine over IIS and I've changed hosts file settings to make it point to https://adfs-example.local/. My ADFS server is VPN connected to my site at the moment, so what I'm saying is the ADFS server itself will never properly resolve an identifier of https://adfs-example.local/ if it ever needs to request something from this URI directly, but things still obviously work once it browser redirects to my site's sign-on page and presents an ADFS token.

Bashing my God forsaken head against the wall some more, I tried adding my own IssuerSigningKeyResolver:

TokenValidationParameters = new TokenValidationParameters
{
    IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, validationParameters) =>
    {
        var store = new X509Store(StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadOnly);
        var cert = store.Certificates.Find(X509FindType.FindByThumbprint, "<My Certificate's Thumbprint>", true)[0];
        store.Close();
        var provider = (RSACryptoServiceProvider)cert.PublicKey.Key;
        return new RsaSecurityKey(provider);
    }
};

Now I have this beauty of an error, and no clue what to do with it:

IDX10213: SecurityTokens must be signed. SecurityToken: '{0}'.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.IdentityModel.Tokens.SecurityTokenValidationException: IDX10213: SecurityTokens must be signed. SecurityToken: '{0}'.

Source Error:

Line 61: Line 62: var validatedToken = (SecurityToken)null; Line 63: var principal = Options.SecurityTokenHandlers.ValidateToken(token, Options.TokenValidationParameters, out validatedToken); Line 64:
var claimsIdentity = principal.Identity as ClaimsIdentity; Line 65:
var ticket = new AuthenticationTicket(claimsIdentity, null);

The handler gets called twice. On the first call, this seems to succeed. It seems the first token is signed. On the second call, it fails. It seems the second token is NOT signed. Why aren't some of my security tokens signed? How can I debug this further? Anyone ever have to deal with something like this?

Related posts

Recent Viewed