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

Adding RS256 #1

Open
ghivert opened this issue Jun 26, 2024 · 9 comments
Open

Adding RS256 #1

ghivert opened this issue Jun 26, 2024 · 9 comments

Comments

@ghivert
Copy link

ghivert commented Jun 26, 2024

Hi!

What would be the cost of adding RS256 algorithm? I'd prefer to have an asymmetric algorithm to check the JWT. 🙂
Unfortunately, Auth0 does not allow to use anything else if you want to be OIDC-conformant.

Thanks for the lib!

@brettkolodny
Copy link
Owner

Sorry for the late response! Happy to add this. I'm not super familiar with RS256, but I've been playing around with it for a bit and have got something working.

How would you imagine this API working? gleam_crypto doesn't support rsa so I think there is something to be answered in terms of how to pass the keys to gwt. My current solution is just accepting the contents of a pem file and doing all the ceremony to turn that into a key and sign. Would that make sense?

@nozomi-core
Copy link

nozomi-core commented Aug 11, 2024

This is something I would be interested in using as well, I don't have any experience with implementing RS256 but for my new project I would need to allow 3rd parties to have comms with RS256. I am ok with the pem file solution as along as the tokens conform to spec and can be verified using public key on systems that don't run gleam.

I would like to add, ideally it would be nice to be able to verify a JWT without a .pem. In my case, the system I use, stores public keys from 3rd party servers in a database directory so the keys are read directly from a string

@brettkolodny
Copy link
Owner

Thanks for the input @nozomi-core ! I need to look more into the API and what different keys look like but when I say pem file contents I mean just the content of those files, you wouldn't need to pass in the path to read from a file or anything.

The WIP looks something like this:

const private_key = <...>
const public_key = <...>

...

let jwt_string =
  gwt.new()
  |> to_signed_string(gwt.RS256(key: private_key))

let verified_jwt = gwt.from_signed_string(jwt_string, gwt.RS256(key: public_key))

I'm not 100% sure if this makes sense or is the best API though because it's treating the public/private key within the API as essentially equivalent. The other option would be having one function per signing algorithm like:

pub fn to_hs256_signed_string(jwt: JwtBuilder, secret: String) -> String { ... }

pub fn to_hs348_signed_string(jwt: JwtBuilder, secret: String) -> String { ... }

pub fn to_hs512_signed_string(jwt: JwtBuilder, secret: String) -> String { ... }

pub fn to_rs256_signed_string(jwt: JwtBuilder, secret: String) -> String { ... }

Which though it's more verbose it may be the way to go since the RS256 version of the function will probably need to return a Result.

@nozomi-core
Copy link

Ah I see. I think in the context of signing with RSA, I am ok with allowing there to be fuzzy idea of what key is public/private, because in the context of RSA signing JWT usually your meant to sign with private and verify with public. Alternatively if you think its better to be clear about private/public you could have a to_signed_with_private and from_signed_with_public but in that case you then you create 2 signing and 2 verify functions and would be nice to just keep them in one place

You can have a look at how Java handles RS256 for some ideas
https://github.com/jwtk/jjwt
https://github.com/PhilJay/JWT

@brettkolodny
Copy link
Owner

After talking with some other people about this it seems like for this feature to be more useful we'll also need JWK support, which means we'll need some way to construct a private key from a JWK. The API I'm leaning towards right now will probably look something like this:

let jwt_builder = gwt.new()

let private_pem_key = key.from_pem("<pem file contents")
let jwt_string = jwt.to_rs256_signed_string(jwt_builder, private_pem_key)

let key_from_jwk = key.from_jwk("<jwk string>")
let jwt = jwt.from_rs256_signed_string(jwt_string, key_from_jwk)

@ghivert
Copy link
Author

ghivert commented Aug 12, 2024

from_jwks with an endpoint? 🥹

@brettkolodny
Copy link
Owner

from_jwks with an endpoint? 🥹

Unfortunately that's probably out of the scope of this library since it'd be impossible to have that function work on both JavaScript and Erlang

@brettkolodny
Copy link
Owner

It's not impossible. Take a look at jwt.io They support RS256 in browser just fine.

The issue is the endpoint part of it, Erlang and JS have incompatible ways of handing async code. I.e. the JS side would have to return a Promise to do the network request. So better to have users of the library get the JWKs themselves and pass them in

@SnakeDoc
Copy link
Contributor

SnakeDoc commented Dec 25, 2024

This would be a very tasty feature to have! 🚀

In my drive-by worthless opinion ( 🤣 ), fetching the jwk can be done at a higher level, either in a convenience/wrapper library or some user code, since this lib is mostly focused on handling of jwt's. Fetching the jwk is fairly simple once provided with the jwks URI anyway.

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

4 participants