Support for JWKS?
carcinocron opened this issue · 9 comments
How would you verify and read a JWT signed by a JWKS? Here are some reference libs from other languages:
https://github.com/auth0/node-jwks-rsa
https://github.com/nov/json-jwt/wiki#decode--verify
It's outlined in the readme: https://github.com/crystal-community/jwt#usage
Just change the algorithm in the example to RS256
So the issue is that i dont see how we can assemble the jkws
pem key via the methods available in openssl_ext
. I currently have not been able to do it at least.
I believe the request is to support verifying a JWT token via the JWKS method. i.e.:
jwt_token = "aaaa.bbbb.cccc"
info = JSON.parse(Halite.get("https://oauth.com/.well-known/openid-configuration").body)
keys = JSON.parse(Halite.get(info["jwks_uri"].as_s).body)
payload, header = JWT.decode(jwt_token, keys, verify: true, validate: true)
More links on the JWKS auth data:
https://8gwifi.org/jwkconvertfunctions.jsp
https://developer.byu.edu/docs/consume-api/use-api/implement-openid-connect/jwks-public-key-documentation
@kalinon if I'm reading this correctly there is a desire to have a helper class that can construct and decode the JWKS structure into something a little more useful?
plus, potentially a HTTP wrapper to help with making requests using those keys?
@kalinon if I'm reading this correctly there is a desire to have a helper class that can construct and decode the JWKS structure into something a little more useful?
plus, potentially a HTTP wrapper to help with making requests using those keys?
Perhaps the HTTP wrapper may be out of scope on this request, at a minimum i think we need to be able to pass a JWKS object to JWT.decode
or perhaps enhance openssl_ext
to allow the creation of RSA public keys from the JWKS information.
I admit i am not an expert in libcrypto or openssl, but it seems being able to set the n
, e
and other variables is required? I did see empty specs in the openssl_ext
for this functionality: https://github.com/randomstate/openssl_ext/blob/master/spec/rsa_spec.cr#L140
The general workflow of JWKS for RS256 is:
- reach out to the
jwks_uri
to get key information - transform the key information into a public key
- use public key to verify token
Number 2 is not currently possible via the jwt
or openssl_ext
libs as far as i can tell.
Here is the RFC: https://tools.ietf.org/html/rfc7517
Any chance of this being added? It will help usecases like integrating google/apple sign ins into our apps or using AWS services using the authenticated api.
For eg.
the google-sign-in's key is at;
pp! pub_key_jwk = HTTP::Client.get "https://www.googleapis.com/oauth2/v3/certs"
pp! pub_key_pem = HTTP::Client.get "https://www.googleapis.com/oauth2/v1/certs"
and I would want to decode like this;
payload, header = JWT.decode(token, pub_key_pem , JWT::Algorithm::HS256)
So after some headache, I have a very simple and reductive JWK to JWT which uses use mod and exp.
require "base64"
pp! jwk = {
"n": "3aOynmXd2aSH0ZOd0TIYd5RRaNXhLW306dlYw26nMp6QPGaJuOeMhTO3BO8Zt_ncRs4gdry4mEaUOetCKTUOyCCpIM2JAn0laN_iHfGKTYsNkjr16FiHWYJmvNJ1Q1-XXjWqNNKMFIKHKtMrsP2XPVD6ufp-lNQmt4Dl0g0qXJ4_Y_CKuP-uSlFWZuJ_0_2ukUgevvKtOZNcbth0iOiFalBRDr-2i1eNSJWOknEphy7GRs-JGPboTdHC7A3b-0dVFGMEMJFhxcEJHJgLCsQGdYdkphLJ5f21gCNdhp3g16H3Cqts2KTXgO4Rr8uhwZx5yiUjTuizD9wc7uDso4UJ7Q",
"use": "sig",
"kty": "RSA",
"kid": "b6f8d55da534ea91cb2cb00e1af4e8e0cdeca93d",
"alg": "RS256",
"e": "AQAB"
}
mod_hex = Base64.decode(jwk["n"]).hexstring
exp_hex = Base64.decode(jwk["e"]).hexstring
pub_key_hex = "30820122300D06092A864886F70D01010105000382010F003082010A0282010100" + mod_hex + "0203" + exp_hex
pp! pub_key = Base64.encode(String.new(pub_key_hex.hexbytes))
This only uses
parameter n: Base64 URL encoded string representing the modulus of the RSA Key.
parameter e: Base64 URL encoded string representing the public exponent of the RSA Key.
out of all possible jwk fields
parameter d: Base64 URL encoded string representing the private exponent of the RSA Key.
parameter p: Base64 URL encoded string representing the secret prime factor of the RSA Key.
parameter q: Base64 URL encoded string representing the secret prime factor of the RSA Key.
parameter dp: Base64 URL encoded string representing the first factor CRT exponent of the RSA Key. d mod (p-1)
parameter dq: Base64 URL encoded string representing the second factor CRT exponentof the RSA Key. d mod (q-1)
parameter qi: Base64 URL encoded string representing the first CRT coefficient of the RSA Key. q^-1 mod p
I made a lib for this here: https://github.com/place-labs/jwks
I also wouldn't mind merging this into this repo.