Failing Okta OAuth2 token validation in AspNetCore

1.5k views Asked by At

I would like to validate a JWT access token from Okta in bearer header on my API, but the validation always fails giving the error below:

2016-12-16 10:12:30.451 +00:00 [Information] Failed to validate the token "eyJhbGciOiJSUzI1NiIsImtpZCI6Il95EVnTTc3MkRHSFdtV19ETDNJYUUyNlUifQ.eFULi1ET2htZFhUalFmcmV5bHdaZkI5aFVobFJ5VlRTenRvRTc1cHVSNUYwUlEiLCJpc3MiOLCJjaWQiOiJZTFpSUUZoWEY1RlpTd2xrbDlwVCIsInVpZCI6IjAwdTkxYXdpZmthZzZYcFE5MGg3Iiwic2NwIjpbIm9wZW5pZCIsInByb2ZpbGUiXX0.K50-cdNI1_m1GLglguCvpiinhxKYNwy0ieAABP7lfO2briaT29mzPeQx07a8F_CyJtQbEtOsPkYviCSK309m8n70WoM51B7FxYTebAxIvWZNrdB_Nsid4YrQHoOoM5b54Fzr4FE-7510TJxvKPg8lWViTQG5cfijE6AL-JXuPYlmdikByZbLwg57P4sUBWByF-pTcRqE2l03VOdkyQOJJ4v22jSUSgKFSYdaXH4ufFt2iTv_sbnNTTtXz4tKLLgfzsKZuxo7-N6-QB7Zuhn7g".
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match 'kid': '_yomDqFziFzjpiI-OZmeDEgM772DGHWmW_DL3IaE26U', 
token: '{"alg":"RS256","typ":"JWT","kid":"_yomDqFziFzjpiI-OZmeDEgM772DGHWmW_DL3IaE26U"}.{"ver":1,"jti":"AT.-DOhmdXTjQfreylwZfB9hUhlRyVTSztoE75puR5F0RQ","iss":"https://dev-606497.oktapreview.com","aud":"https://dev-606497.oktapreview.com","sub":"[email protected]","iat":1481883065,"exp":1481886665,"cid":"YLZRQFhXF5FZSwlkl9pT","uid":"00u91awifkag6XpQ90h7","scp":["openid","profile"]}'.
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__1.MoveNext()

I'm wondering if the reason could be, that the public key 'kid' in token is different than the public key in Keys endpoint on Okta?

{"keys": [{"alg": "RS256","e": "AQAB","n": "wtkBXocJLBE-ArN56pLzSiR3x2w99R2d_rlCpFN__3k1I6P0vcfE4SKwoafzucaG-kEwy9pn4p49z0O24UHX0NmdxOMhyFmJsfss0tK0AkBhXB-e9kk5r316ePRtb7eo8uAnjNP7w2T6sSqwdppw7I8NQa4KrFIYFVDx4xDcYMfnGrKjKFdghxSpG2dP7vcQsjJHkMyEHYj7nTTyplReX21_Et2F5zHqvqQZ1JRuL_Ol-JrSEeM0Hznpb7kpggnFUA_xnzcR4AhT5P2WNNNenlfurjM_AN1ymV8DT04Tx7tp6G60N1AkDw4t4Q0LfuevQ","kid": "gtUiz-YdlCSRpr0Ue7LRuEtqgVqRmDWpe5ZuvBaWgVk","kty": "RSA","use": "sig"}]}

This is the setup on WebAPI:

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme,
    Audience = "http://api.azurewebsites.net",
    Authority = "https://dev-606497.oktapreview.com"
});
2

There are 2 answers

1
Sohaib Ajmal On BEST ANSWER

In order to get keys for access token via public keys url some features need to be enabled for your Okta org. Please send email to [email protected]

1
remanc On

You are correct - the kid is different from the public key - it's the key id that identifies which public key to use from the Keys response. If we look at the response example for v1/keys:

"keys": [
  {
    "alg": "RS256",
    "e": "AQAB",
    "n": "iKqiD4cr7FZKm6f05K4r-GQOvjRqjOeFmOho9V7SAXYwCyJluaGBLVvDWO1XlduPLOrsG_Wgs67SOG5qeLPR8T1zDK4bfJAo1Tvbw
          YeTwVSfd_0mzRq8WaVc_2JtEK7J-4Z0MdVm_dJmcMHVfDziCRohSZthN__WM2NwGnbewWnla0wpEsU3QMZ05_OxvbBdQZaDUsNSx4
          6is29eCdYwhkAfFd_cFRq3DixLEYUsRwmOqwABwwDjBTNvgZOomrtD8BRFWSTlwsbrNZtJMYU33wuLO9ynFkZnY6qRKVHr3YToIrq
          NBXw0RWCheTouQ-snfAB6wcE2WDN3N5z760ejqQ",
    "kid": "U5R8cHbGw445Qbq8zVO1PcCpXL8yG6IcovVa3laCoxM",
    "kty": "RSA",
    "use": "sig"
  },
  ... more
]

We can see that there is a kid property - if you're doing it manually, you have to iterate over the keys to find the key that matches the kid in your /token response, and use that in your JWT validation.

There are a couple samples that demonstrate this validation flow in ASP.net - for example, here's the relevant code in an Authorization Code flow sample.

It's also probably helpful to check out our Validating Access Tokens doc for a more general understanding of the flow.

Update: Just noticed that the kid you're trying to lookup is not in the keys response - this is as designed for access tokens returned through OIDC (they are opaque). There are a couple options here:

  1. If OpenId Connect, you have to validate the returned access token through the /userinfo endpoint
  2. Or, if you're looking into API Access Management, you'll need to contact [email protected] to turn on that feature for you (as Sohaib suggested). This will expose the public keys for your access tokens through the keys endpoint.