3

I'm using Okta for identity management. As the client in authorization flow, I send an authorize request to Okta. This works successfully, and I get a JWT payload. I want to verify the JWT signature, so I make another call to Okta in order to fetch the keys. However, the key ids (kids) do not match and verification fails.

Initial authorize request:

https://{{site}}.okta.com/oauth2/v1/authorize
  ?scope=openid
  &response_type=id_token
  &client_id={{client_id}}
  &redirect_uri={{redirect_url}}
  &nonce=4euiv0v52at3la15e7qlu1mt43
  &state=7c92bqulrmdk2jk0ro9rd3mf5j

Response is a 403, redirecting me to:

{{redirect_url}}/id_token={{id_token}}

The header of the id_token is decoded into:

{
  "alg": "RS256",
  "kid": "2YKtkekCjCRWN0YqGsjUrNwIQaxGg5ahfHW0_fK8t64"
}

So far so good. I know that the authorization has succeeded. Time to validate the JWT.

However, when this is followed up with:

https://{{site}}.okta.com/oauth2/v1/keys

Or

https://{{site}}.okta.com/oauth2/v1/keys?clientId={{client_id}}

(they both return the same response), I get back this:

{
  "keys": [
    {
      "alg": "RS256",
      "e": "AQAB",
      "n": "gv1rI9A7mrOoViJZTzUfiZl7YdEzLEofvRoVbXCgeW7aOmoKcAkWGHvqNRGoFgi8auV5b_TSgTXKq_TV1fz643hpAtba3V0Uw2lXchTbqXpmVRYXI1t4FIwRMXLe4Q-kcvp9la21e3D1lszjdPbFNX5GLAhrCW0Thu2HYbTLg6TbDTMaiQCMo15hek0JgZqRGzCkt9kINnwPVLXV_bkSh_fHWo_6G1L0MKYYQcgE6zvPlULLek98-yZ6Nlg6nJUY9nHn0qjhzqqq-bz_Vin8qi3Bt7SjUKwk7HbaugM84AEgDxYE5JgsaALIl5SgIc3GgFEc69qKWymoD-w1a8f1HQ",
      "kid": "SOxFkBSLWefjlZoDI49Hk0nqlYtC28cjhTlVAYEzAxs",
      "kty": "RSA",
      "use": "sig"
    }
  ]
}

Where the kid does not match what I received in the original response.

Where is my mistake?

Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
Magua
  • 258
  • 1
  • 9
  • This is a good question! I took the liberty of editing it slightly to make it more readable for other folks who might run into this. Let me know if you think my edit changed the meaning of your question too much. – Nate Barbettini Sep 06 '17 at 22:54

2 Answers2

4

You need to create an authorization server and use it as the endpoint, for example:

https://{{site}}.okta.com/oauth2/{authorizationServerId}/v1/authorize

You should also be able to use the default one:

https://{{site}}.okta.com/oauth2/default/v1/authorize

Note that this is different than the route you were using (which does not specify an authorization server):

https://{{site}}.okta.com/oauth2/v1/authorize

You should specify an authorization server in your case (like example 1 and 2 above), for both OAuth 2.0 and OpenID Connect.

Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
Matt Raible
  • 8,187
  • 9
  • 61
  • 120
  • Thanks for the response. I see that this is true for the OAuth2 flow, but according to the docs for the OpenID flow, there is no authorization server specified -- Okta is the only one. Checking at /.well-known/openid-configuration shows the URLs that I am using, with no authorization server specified in the path. – Magua Sep 05 '17 at 16:32
  • What language are you using to validate the JWT? We might have a library that can help you. – Matt Raible Sep 05 '17 at 22:08
  • @Magua The documentation does seem to imply that you can't use OIDC with a custom authorization server, but you can. Both OAuth 2.0 and OpenID Connect work with a custom AS. As Matt said, you may already have a custom AS called `default`. – Nate Barbettini Sep 06 '17 at 22:53
  • @MattRaible I'm using jose4j (Java). – Magua Sep 07 '17 at 18:26
  • You might try using using Okta's JWT Verifier for Java: https://github.com/okta/okta-jwt-verifier-java – Matt Raible Sep 07 '17 at 18:57
  • The JWT seems to be entirely valid -- I can decode the id token and that all works ok. However, I can't find a public key to verify the signature unless kid's match. Skipping signature verification is not an option for me. – Magua Sep 07 '17 at 20:11
  • @Magua What about when you use `https://{{site}}.okta.com/oauth2/default/v1/authorize` instead? – Nate Barbettini Sep 07 '17 at 23:36
  • The application ID is the last segment in the decoded JWT issuer uri. This will allow you to retrieve the ID dynamically without hardcoding in your application. – mccainz Feb 08 '21 at 01:36
1

The problem was that this account was setup with pinned, not rotating keys. oauth2/v1/keys requires the client id to be passed in as a parameter if you are setup with pinned keys; the correct parameter name is "client_id", not "clientId." This results in the expected output.

Magua
  • 258
  • 1
  • 9