We are trying to use ruby-jwt to encode/decode HS256 signed JWTs using kids to indicate the record in a keystore that represents the key to use, but it seems like we need to do 2 decodes for verification. One decode not using a secret or indicating to ruby-jwt that we want to verify the JWT just so we can scrape the kid out of it, then a second decode that passes in the secret that the kid represents, and the flag to verify the JWT. It's a minor thing and b64 is cheap, but is doing 2 decodes really the approach that needs to be taken for HMAC JWTs with kid in them? Is this a limitation of the library, or a gap in knowledge on how to work with kids with HMAC?
Create JWT with HS256
keys = {'31b88f20-8edd-49fe-a839-57b8f681888c' => 'mysecret'}
payload = {user_id: 99, kid: keys.first.first, exp: 5.years.from_now.to_i}
token = JWT.encode(payload, keys.first.second, 'HS256')
Verify JWT
# Get the kid from the jwt so we can look up what secret to use to verify it by the kid
decoded_jwt = JWT.decode(token, nil, false)
kid = decoded_jwt[0]['kid']
secret = keys[kid]
# decode the jwt again with the secret, and verify it
decoded_jwt = JWT.decode(token, secret, true)
# => [{"user_id"=>99, "kid"=>"31b88f20-8edd-49fe-a839-57b8f681888c", "exp"=>1865946624}, {"alg"=>"HS256"}]
It works but trying to see if there is a better approach. Thanks!
You can avoid the double decoding by using JSON Web Key Set (JWKS).
To implement this process using JWKS and HMAC you can do so as follows.
Encoding:
Export Keys:
Since HMAC is a symmetric key the Key Type (
kty) needs to be set to 'oct' See RFC7518 at 6.1, and the Key Value (k) is set to the secret See RFC7518 at 6.4.1Decoding: