Keats / jsonwebtoken

JWT lib in rust
MIT License
1.69k stars 271 forks source link

error loading google/firebase public key #127

Closed dakom closed 2 years ago

dakom commented 4 years ago

The docs at https://firebase.google.com/docs/auth/admin/verify-id-tokens suggest getting the public key via https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com

I tried loading this in via DecodingKey::from_rsa_pem but no luck so far.

Does the x509 string need to be converted to something else before loading?

Keats commented 4 years ago

x509 are not supported currently.

maylukas commented 4 years ago

@dakom I ran into the same issue and was able to create a key from the Firebase RSA components. I wrote an article about this, since I found multiple people asking similar questions: https://medium.com/@maylukas/firebase-token-authentication-in-rust-a1885f0982df

@Keats If you have some time to review my solution I would be very happy :)

Keats commented 4 years ago

There's a typo in the code example: UnkownKeyAlgorithm

Overall JwkVerifier is something I thought adding to jsonwebtoken but wasn't sure on the best way to implement it. If we can find a generic enough struct/impl, it could be added directly in the lib.

The jsonwebtoken library only verifies the expiry date and signature, so we need to check the audience, and the issue matches afterward.

You can check it with jsonwebtoken automatically: https://docs.rs/jsonwebtoken/7.1.1/jsonwebtoken/struct.Validation.html

Great article otherwise!

maylukas commented 4 years ago

@Keats Oh thanks. Did not see it. I will change it to use jsonwebtoken to verify the issuer and audience. I won't have time today but some day next week. Thanks for checking the article 😊

maylukas commented 4 years ago

@Keats: Okay. I updated the post and the example on Github. Bit late but still 😆

Overall JwkVerifier is something I thought adding to jsonwebtoken but wasn't sure on the best way to implement it. If we can find a generic enough struct/impl, it could be added directly in the lib.

Maybe it would be possible to add a key_id parameter to the DecodingKey struct. Then it would be possible to select the proper key from a list of provided keys based on the specified key id in the token header. The JwkVerifier struct could then have a generic parameter for the claims to deserialize and could be initialized with the verification parameters.

Regarding verification: Does it maybe make sense to make the DecodingKey aware of its algorithm? Currently, it is set in the Verification struct but I think it might be better to move it to the DecodingKey. Then a token can just be verified with a DecodingKey and no further information is required by the verifying component as the key will already provide the proper algorithm. So the DecodingKey could receive a "verify_signature" method and the Verification would then just validate metadata (expiry, issuer, audience, ...).

If no key id is provided which might be the case if a shared secret is used, the verifier would just use the first one.

Just some random ideas 😁 I don't know if any of these suggestions are worth anything since I don't know your library in detail but maybe it helps.

FrancoisChabot commented 3 years ago

FWIW, you can simply use the openssl crate to extract the public key from the x509:

let certificate = openssl::x509::X509::from_pem(v.as_bytes())?;
let pem_bytes = certificate.public_key()?.rsa()?.public_key_to_pem()?;

However, for some reason, I couldn't get this to work with der, and had to go through pem.

alertedsnake commented 3 years ago

@FrancoisChabot this is exactly what I needed, thanks!

Roms1383 commented 3 years ago

@FrancoisChabot dude, you made my day ! thanks !! 👍 @maylukas your article on Medium is dope too

btielen commented 3 years ago

Instead of using the openssl crate, you can also use the command line to extract the public key from the pem certificate file:

openssl x509 -pubkey -noout -in certificate.pem

This will output something like:

-----BEGIN PUBLIC KEY-----
xxx
-----END PUBLIC KEY-----

Then use it in your code like:

let public_key = b"-----BEGIN PUBLIC KEY-----
xxx
-----END PUBLIC KEY-----";

DecodingKey::from_rsa_pem(public_key).unwrap()
ech0r commented 2 years ago

FWIW, you can simply use the openssl crate to extract the public key from the x509:

let certificate = openssl::x509::X509::from_pem(v.as_bytes())?;
let pem_bytes = certificate.public_key()?.rsa()?.public_key_to_pem()?;

However, for some reason, I couldn't get this to work with der, and had to go through pem.

@FrancoisChabot You are a lifesaver!