Search code examples
c#azurejwtazure-logic-appsadaptive-cards

How to validate jwt token from different issuer


I'm using actionable messages (with Outlook web app) to call an Logic App. Therefore I am getting an Bearer token in the request:

"Action-Authorization": "Bearer eyJ0eXAi..."

Callstack: Outlook web app -> Logic App -> my endpoint hosted in azure

Now I tried to validate the token with jwt.io but getting an Issue that the Signature is invalid. So I tried to validate it in c# with the JwtSecurityTokenHandler.

I tried to add https://substrate.office.com/sts/ to the issuer list, but it seems like the validation don't even get there.

I'm using the following code to validate the jwt token issued by office.com:

bool IsAuthorized(HttpActionContext actionContext)
        {
            var valid = base.IsAuthorized(actionContext);

            // Custom handle for Bearer token, when invalid from base-class
            if (!valid && actionContext.Request.Headers.Authorization.Scheme == "Bearer")
            {
                var jwt = actionContext.Request.Headers.Authorization.Parameter;
                var th = new JwtSecurityTokenHandler();
                var sjwt = th.ReadToken(jwt) as JwtSecurityToken;                

                var validationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = false,
                    //IssuerSigningToken = sjwt,
                    ValidateActor = false,
                    ValidateAudience = false,
                    ValidateIssuer = true,
                    ValidateLifetime = true,
                    ValidIssuers = new[] { "https://substrate.office.com/sts/" },
                    ValidAudiences = new[] {"https://XXX.logic.azure.com"}
        };

                SecurityToken validatedToken;
                try
                {
                    th.ValidateToken(jwt, validationParameters, out validatedToken);
                }
                catch (Exception ex)
                {
                    return false;
                }
            }

            return valid;
        }

Here is my JWT token:

JWT Token

I am getting the exception:

IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier
    (
    IsReadOnly = False,
    Count = 2,
    Clause[0] = X509ThumbprintKeyIdentifierClause(Hash = 0x818...),
    Clause[1] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
    )
', ...

Even though I set ValidateIssuerSigningKey = false.

Is there a way to accept https://substrate.office.com/sts/ as a valid issuer?


Solution

  • The exception says that the "Signature validation failed". To resolve this problem we can't just add the wanted valid issuer to ValidIssuers, we need the to verify that the token is issued from the issuer itself.

    Especially for this case with office.com being the issuer I found the expected key (JWK - JSON Web Key) here: https://substrate.office.com/sts/common/discovery/keys (also https://substrate.office.com/sts/common/.well-known/openid-configuration)

    Here is the working code:

    bool IsAuthorized(HttpActionContext actionContext)
            {
                var valid = base.IsAuthorized(actionContext);
    
                // Custom handle for Bearer token, when invalid from base-class
                if (!valid && actionContext.Request.Headers.Authorization.Scheme == "Bearer")
                {
                    var jwt = actionContext.Request.Headers.Authorization.Parameter;
                    var th = new JwtSecurityTokenHandler();
    
                    var validationParameters = new TokenValidationParameters
                    {
                        ValidateAudience = false,
                        ValidateLifetime = true,
                        ValidateIssuer = true,
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new JsonWebKey(GetJWK()),
                        ValidIssuers = new[] { "https://substrate.office.com/sts/" }
                    };
    
                    Microsoft.IdentityModel.Tokens.SecurityToken validatedToken;
                    try
                    {
                        var claims = th.ValidateToken(jwt, validationParameters, out validatedToken);
                        valid = true;
                    }
                    catch (Exception ex)
                    {
                        valid = false;
                    }
                }
    
                return valid;
            }
    
            // Get the token from configuration
            private string GetJWK()
            {
                return ConfigurationManager.AppSettings["ida:jwks_json"];
            }
    

    In the appsettings I put the RSA key from the website for validating the token, it looks like:

    {"kty":"RSA","use":"sig","kid":"gY...","x5t":"gY...","n":"2w...","e":"AQAB","x5c":["MII..."]}