Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to validate the access token and extract the subject from it using publicly available knowledge only? #17

Open
theirishpenguin opened this issue Dec 29, 2016 · 1 comment

Comments

@theirishpenguin
Copy link

theirishpenguin commented Dec 29, 2016

Hi nov, thanks for a great library and samples.

I am trying to understand how I can validate and get the "subject" out of an access token generated by your openid_connect_sample server - assuming that all I have access to is the access token itself and HTTP://THE_OPENID_CONNECT_SAMPLE_SERVER/.well-known/openid-configuration file, which is publicly available on the server.

Why? I am trying to follow the validating and accessing the subject in the access token as outlined at http://stackoverflow.com/a/34378565/427805 (particularly step 4). Or perhaps put more simply - how would I carry out step 5 in the Implicit Flow in this answer http://security.stackexchange.com/a/130478

I have seen your excellent example for the "Decode and Verify" section in https://github.com/nov/json-jwt/wiki - but that is for ID Tokens as opposed to access tokens. How would I do the same for an access token? eg.

access_token = 'some token string'
idp_jwks_url = 'http://localhost:3007/jwks.json'
json = RestClient.get(idp_jwks_url)
hash = JSON.parse(json)
jwk_set = JSON::JWK::Set.new(hash)
success = JSON::JWT.verify access_token_string, jwk_set #???
subject = JSON::JWT.get_subject access_token_string, jwk_set #???

Maybe there is a gap in my knowledge and I need something else? If so what does the openid_connect server expose that would let me achieve this or what do I need to set up? Selfishly, I want to know the answer :-) but I suspect this would be useful for others people - as it closes the loop on the Implicit Flow approach.

UPDATE:

  • I do see some more examples at another one of your libraries at https://github.com/nov/json-jwt but here I don't quite understand how the private/public keys relate to the idp_jwks_url - if at all?
  • Did the openid_connect_sample server sign the id token or access token (or something else) before it returned an access token and id token to a JS client - so that a public key can then be used to validate the token and extract the subject?
@theirishpenguin
Copy link
Author

Okay, I think I am starting to understand this process a bit better.

This is no simple standard cryptographic verification of an access token - because the access token can be anything that one wishes it to be (as long as the REST API that will ultimately consume it can make sense of it).

In the openid_connect_sample code we can see the line that does the token creation at line 54 of lib/authorization_endpoint.rb (at time of writing)...

 account.access_tokens.create!(client: @client)

When we dig into the setup() method of app/models/access_token.rb, which determines exactly what that token will be, we see that it is generated using SecureRandom.hex(32)

 def setup                                                                     
    self.token      = SecureRandom.hex(32)                                      
    self.expires_at = 24.hours.from_now                                         
  end

So there is nothing encoded into this token that cannot be cryptographically verified.

We can see from other conversations that some people have been tempted to use the ID token to access REST API's at IdentityServer/IdentityServer3#2015

But reading through all of that thread hammers home the point that the ID Token is very much for the client (eg. JS app) to interact with the Authorization server - not as an API token. In the Access Token is the only thing that should be used for the client to talk tot the API server (even though there is a lot of conflicting information available - as can be seen from the discussion at the above link).

So, for anyone reading this, one potential solution that could be evaluated is to generate a new ID Token (perhaps re-using the existing jwks that the Authorization Server already exposes, though it would make for easier key rotation to use and publish a different set of jwks) and put that ID Token into the (access) token field. eg.

 def setup                                                                     
    self.token      = your_method_to_generate_a_new_id_token                                      
    self.expires_at = 24.hours.from_now                                         
  end

For example you could use the "Generate" example under "OpenID Connect ID Token" https://github.com/nov/json-jwt/wiki (you would need to have another private key available to do this if you plan to publish a different set of jwks keys). Even though we're using the ID Token technique of generating the access key - it really is just an access key. It will get passed to the API by your JS client (or whatever). Then your API can use the crypographic mechanism of verifying the token, its expiry, it's claims, which needing to talk to the Authorization server.

In summary

  • the ID Token that we get out of the box with openid_connect_sample (which is just a sample server) is just for the JS client and the claims, scopes, etc it encodes are only for the JS client.
  • the Access token that we discussed generating in the previous paragraph using the ID token generation technique will act like a valid JWT token and can be parsed and verified on the API server. The claims, scopes and other details it encodes are only for the API server.

Thanks again for all these projects @nov!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant