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

Support for JWK in-lieu of rsa_public #158

Closed
caldwecr opened this issue Jul 29, 2016 · 8 comments
Closed

Support for JWK in-lieu of rsa_public #158

caldwecr opened this issue Jul 29, 2016 · 8 comments

Comments

@caldwecr
Copy link

@caldwecr caldwecr commented Jul 29, 2016

So AWS Cognito User Pools went GA today. Working through a lot of the details. In particular the process of verifying the integrity of the Id and Access token types that Cognito returns. Both of these are JWT tokens and can ultimately be verified using ruby-jwt - BUT to do that requires first converting the JWK format that Amazon provides the Cognito public keys for an individual AWS account's UserPool.

Here were the steps I ended up following ...

  1. Get the public key in JWK form from aws following directions
  2. Use website to convert to rsa form link
  3. Save rsa output to pem and chmod 600
  4. rsa_public = OpenSSL::PKey::RSA.new File.read 'aws1.pem'
  5. decoded_token = JWT.decode token, rsa_public, true, algorithm: 'RS256'

So a couple things -

  • If I were to contribute the code could we fit this into jwt directly?
  • Ideally I'd just enter the Cognito known jwks.json path or uri and the appropriate key (by kid) would automatically be used. Is this an acceptable extension of jwt or is this seen as too vendor specific? Similar to this
  • Is this already in jwt and I just didn't look hard enough?
  • It seems like potato salad's jose implementation can accomplish this, but the MPL2 is a bit more complicated than your MIT license, are there other good existing JWK choices?
@caldwecr
Copy link
Author

@caldwecr caldwecr commented Jul 29, 2016

Maybe using json-jwt's JWK implementation is the fastest path?

@ce07c3
Copy link

@ce07c3 ce07c3 commented Mar 28, 2017

@caldwecr yes, but I believe they are susceptible to https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ as the library does not require specifying an algorithm. Would be good if this library were to implement it.

@dmdeller
Copy link

@dmdeller dmdeller commented Oct 12, 2017

In case anyone (like me) came across this issue while trying to implement AWS Cognito, here is a solution that uses the json-jwt gem instead of Node + filesystem as in the above example.

  1. Bundle/require json-jwt
  2. Get the public key in JWK form from AWS following directions
# key_hash_from_aws looks like {"alg"=>"...", "e"=>"...", kid=>"...", "kty"=>"...", "n"=>"...", "use"=>"..."}
jwk = JSON::JWK.new(key_hash_from_aws)

# returns an OpenSSL::PKey::RSA, which is what JWT.decode expects
rsa_public = jwk.to_key

decoded_token = JWT.decode(token, rsa_public, true, algorithm: 'RS256')
@rabajaj0509
Copy link
Contributor

@rabajaj0509 rabajaj0509 commented Oct 2, 2019

Quick question @dmdeller, what if I have multiple keys in the key_hash_from_aws in the above given example?

Do i need to then loop upon the rsa_public = jwk.to_key step for every key and then again loop upon decoded_token = JWT.decode(token, rsa_public, true, algorithm: 'RS256') with all the public keys to identify which one it would decode with?

Or there is a better way of doing that?

@robinbortlik
Copy link

@robinbortlik robinbortlik commented Oct 8, 2019

Hi @rahulbajaj0509 , I was able to easily validate keys by using

KEYS = [
  {
    alg: "RS256",
    e: "AQAB",
    kid: "ZZmKb1+XXXXXX",
    kty: "RSA",
    n: "XXXXXXXXXX",
    use: "sig"
  },
  {
    alg: "RS256",
    e: "AQAB",
    kid: "rFjv01poeXXXXXXX",
    kty: "RSA",
    n: "XXXXXXXX",
    use: "sig"
  }
]

JWT.decode(jwt_token, nil, true, { algorithms: ['RS256'], jwks: { keys: KEYS } })

In any case, check: https://github.com/jwt/ruby-jwt/#json-web-key-jwk

@la-jamesh
Copy link

@la-jamesh la-jamesh commented Feb 19, 2020

It's interesting to note in the example above that the decode function is expecting the keys as a hash of symbols. (I realize its defined like that above but I still figured it would be smart enough to differentiate) This means fetching your jwks keys from a public endpoint which returns a hash of string keys will not work. Something like..

keys = HTTParty.get(ENV['JWKS_URL']) # { "keys": [{ "kid": "..." }, ...]}
JWT.decode(jwt_token, nil, true, { algorithms: ['RS256'], jwks: keys })

will throw a key not found error or something similar.

Where as..

keys = HTTParty.get(ENV[''JWKS_URL']) # { "keys": [{ "kid": "..." }, ...]}
JWT.decode(jwt_token, nil, true, { algorithms: ['RS256'], jwks: keys.deep_symbolize_keys! }) # Note deep_symbolize_keys is a rails active support function.

Will work. Took me a while to figure out why I was seeing that error. Hope that saves someone some time.

@anakinj
Copy link
Member

@anakinj anakinj commented Dec 1, 2020

JWKs are now a part of the gems and the symbol vs keys in addressed in the next release.

Closing this now. Feel free to open new issues with suggestions for future improvements are welcome.

@anakinj anakinj closed this Dec 1, 2020
@n1nj4-r4cc00n
Copy link

@n1nj4-r4cc00n n1nj4-r4cc00n commented Mar 30, 2021

It didn't work unless you symbolize the name of the keys, thx for the hints above, in my case I used this (in case someone else follows the same path):

JWT.decode(token, nil, true, {jwks: {keys: JSON.parse(Net::HTTP.get(URI(COGNITO_SIGNATURE_URL)), :symbolize_names => true)[:keys]}, algorithm: 'RS256' })

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

Successfully merging a pull request may close this issue.

None yet
9 participants