Keats / jsonwebtoken

JWT lib in rust
MIT License
1.61k stars 253 forks source link

Help regarding decoding key and validating. #394

Closed Bryson14 closed 3 weeks ago

Bryson14 commented 3 weeks ago

I am working with a company IdP that creates a JWT token we can authenticate with. They are using RS256 and give out the public certificate key in the form of a .crt file in order to validate the keys from them.

The .crt file looks like this:

-----BEGIN CERTIFICATE-----
wNkHXWcR/u1SBeAn5JOFJDuHBL9WF1J9JQDQjow MORE STUFF
-----END CERTIFICATE-----

From my best knowledge, this is in PEM form and I'm saving that .crt to a .pem.

I've verified that the token and the public key do work using the jwt.io website.

I've been stuck for a few days now and this seems mind-numbing! haha

Here is what I've tried using this test function:

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct TokenClaim {
    pub sub: String,
    pub exp: usize,
    pub iss: String,
}

#[test]
fn test_jwt_token_decoder() {
let example_token = "SOMETOKEN";
let secret = include_bytes!("../tests/test.pem");
print!("header: {:?}", decode_header(example_token).expect("Failed to just decode header"));
let decoding =
    &DecodingKey::ed(secret);

let token_claims =
    decode::<TokenClaim>(example_token, decoding, &Validation::new(Algorithm::RS256))
        .expect("Failed to decode token");

let expected_claims = TokenClaim {
    sub: "123456789@stuff.com".to_string(),
    iss: "https://idam.gehealthcloud.io/t/genaidev.app/oauth2/token".to_string(),
    exp: 1715560289,
};

assert_eq!(token_claims.claims, expected_claims);
}
Decoding Key Error MSG
::from_secret Invalid Algorithm
::from_rsa_pem InvalidKeyFormat
::from_ec_pem InvalidKeyFormat
::from_ed_pem InvalidKeyFormat
::from_rsa_der Invalid Signature
::from_ec_der Invalid Algorithm
::from_ed_der Invalid Algorithm

For the from_rsa_der, I have this error printout from the test run:

---- middleware::auth::tests::test_jwt_token_decoder stdout ----
header: Header { typ: None, alg: RS256, cty: None, jku: None, jwk: None, kid: Some("ZWViOTc1MjlkOGUyYTViOWZiOTRiZjBmMzEyOWJhZDJhZGI5MmQ3ZWE3YjE4YmJjNjUxMmQ1MzY2MWVmM2Q3YQ_RS256"), x5u: None, x5c: None, x5t: Some("ZWViOTc1MjlkOGUyYTViOWZiOTRiZjBmMzEyOWJhZDJhZGI5MmQ3ZWE3YjE4YmJjNjUxMmQ1MzY2MWVmM2Q3YQ"), x5t_s256: None }thread 'middleware::auth::tests::test_jwt_token_decoder' panicked at src\middleware\auth.rs:257:18:
Failed to decode token: Error(InvalidSignature)

Here is a redacted screenshot from jwt.io token

What am i doing wrong? I been reading the readme but must be missing something.

Bryson14 commented 3 weeks ago

Some options that maybe I could get some guidance is:

  1. will the library give an error if there is an audience in the claims but I don't check it
  2. Do i need to convert all the data in the claim to a struct, so do I need to add more properties to my TokenClaim
  3. the exp was fine when I was checking this so I don't think that is the issues
  4. Perhaps the format of the .pem file i have is fine, but I don't thin that is likely since https://jwt.io did fine with it.
Bryson14 commented 3 weeks ago

By adding an audience and allowing for insecure signature validation, I got it to work and pass the test, which is a start but not the solution.

let decoding = &DecodingKey::from_rsa_der(secret);
let mut validation = Validation::new(Algorithm::RS256);
validation.set_audience(&["my-app-id"]);
validation.insecure_disable_signature_validation();
validation.validate_exp = false;

I'm still leary of this DecodingKey::from_rsa_der() is the right way to go

Bryson14 commented 3 weeks ago

SOLVED The issue here is that a .crt is not .pem file. It can be converted however,

The issue here seems to be that the DecodingKey expects a key. RSA public keys usually look like this:

-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----

The certificate here is very simplified a "wrapper" over the public key. With the openssl tool you can extract it. The command for this looks like this:

openssl x509 -pubkey -noout -in cert.pem  > pubkey.pem

After converting it to a formatted .pem file, the ::from_rsa_pem() should work.