Skip to content

Workaround when WsFederationMessage.GetToken() returns a string with "
" in SAML that breaks signature validation.

BrentSchmaltz edited this page Oct 19, 2020 · 1 revision

White space is significant between elements when obtaining the C14n (https://www.w3.org/TR/xml-c14n/) octets that the signature is generated from. If the original xml had this whitespace, then the C14n octets which are used to generate the signature must have the octets.

Sometimes as the xml flows through nodes, whitespace is added (in this case carriage return \r). C14n removes whitespace inside elements, orders attributes, fixes namespaces, and other stuff. But does not remove the outer element whitespace. Removing whitespace automatically is breaking.

We plan on adding a switch that will allow control for ignoring whitespace between elements. In the meantime, the following logic is a suggested workaround for asp.net users.

In startup.cs replace the SamlSecurityTokenHandler with the code below that retries the validation if SecurityTokenInvalidSignatureException is thrown.

The same code would work Saml2 tokens.

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddAuthentication(options =>
    {
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddWsFederation(options =>
    {
        options.SecurityTokenHandlers.Clear();
        options.SecurityTokenHandlers.Add(new CustomSamlSecurityTokenHandler());
        options.SecurityTokenHandlers.Add(new Saml2SecurityTokenHandler());
        options.SecurityTokenHandlers.Add(new JwtSecurityTokenHandler());
        options.MetadataAddress = "<METADATA address here: https://login.microsoftonline.com/<YOUR tenant>/FederationMetadata/2007-06/FederationMetadata.xml";
        options.Wtrealm = "<YOUR ClientId>";
        options.Wreply = "<Reply url registered with AAD>";
    });

     ...
}

public class CustomSamlSecurityTokenHandler : SamlSecurityTokenHandler
{
    public override ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
    {
        if (securityToken.Contains("&#xD;"))
        {
            string securityTokenTrimmed = securityToken.Replace("&#xD;", "");
            try
            {
                return base.ValidateToken(securityTokenTrimmed, validationParameters, out validatedToken);
            }
            catch (Exception ex)
            {
                if (!(ex is SecurityTokenInvalidSignatureException))
                    throw;
            }
        }

        return base.ValidateToken(securityToken, validationParameters, out validatedToken);
    }
}
Clone this wiki locally