GithubHelp home page GithubHelp logo

jsonwebtoken's Introduction

jsonwebtoken

API documentation on docs.rs

See JSON Web Tokens for more information on what JSON Web Tokens are.

Installation

Add the following to Cargo.toml:

jsonwebtoken = "9"
# If you do not need pem decoding, you can disable the default feature `use_pem` that way:
# jsonwebtoken = {version = "9", default-features = false }
serde = {version = "1.0", features = ["derive"] }

The minimum required Rust version (MSRV) is 1.67.

Algorithms

This library currently supports the following:

  • HS256
  • HS384
  • HS512
  • RS256
  • RS384
  • RS512
  • PS256
  • PS384
  • PS512
  • ES256
  • ES384
  • EdDSA

How to use

Complete examples are available in the examples directory: a basic one and one with a custom header.

In terms of imports and structs:

use serde::{Serialize, Deserialize};
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};

/// Our claims struct, it needs to derive `Serialize` and/or `Deserialize`
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    sub: String,
    company: String,
    exp: usize,
}

Claims

The claims fields which can be validated. (see validation)

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    aud: String,         // Optional. Audience
    exp: usize,          // Required (validate_exp defaults to true in validation). Expiration time (as UTC timestamp)
    iat: usize,          // Optional. Issued at (as UTC timestamp)
    iss: String,         // Optional. Issuer
    nbf: usize,          // Optional. Not Before (as UTC timestamp)
    sub: String,         // Optional. Subject (whom token refers to)
}

Header

The default algorithm is HS256, which uses a shared secret.

let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref()))?;

Custom headers & changing algorithm

All the parameters from the RFC are supported but the default header only has typ and alg set. If you want to set the kid parameter or change the algorithm for example:

let mut header = Header::new(Algorithm::HS512);
header.kid = Some("blabla".to_owned());
let token = encode(&header, &my_claims, &EncodingKey::from_secret("secret".as_ref()))?;

Look at examples/custom_header.rs for a full working example.

Encoding

// HS256
let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref()))?;
// RSA
let token = encode(&Header::new(Algorithm::RS256), &my_claims, &EncodingKey::from_rsa_pem(include_bytes!("privkey.pem"))?)?;

Encoding a JWT takes 3 parameters:

  • a header: the Header struct
  • some claims: your own struct
  • a key/secret

When using HS256, HS384, or HS512, the key is always a shared secret like in the example above. When using RSA/EC, the key should always be the content of the private key in PEM or DER format.

If your key is in PEM format, it is better performance wise to generate the EncodingKey once in a lazy_static or something similar and reuse it.

Decoding

// `token` is a struct with 2 fields: `header` and `claims` where `claims` is your own struct.
let token = decode::<Claims>(&token, &DecodingKey::from_secret("secret".as_ref()), &Validation::default())?;

decode can result in errors for a variety of reasons:

  • the token or its signature is invalid
  • the token had invalid base64
  • validation of at least one reserved claim failed

As with encoding, when using HS256, HS384, or HS512, the key is always a shared secret like in the example above. When using RSA/EC, the key should always be the content of the public key in PEM (or certificate in this case) or DER format.

In some cases, for example if you don't know the algorithm used or need to grab the kid, you can choose to decode only the header:

let header = decode_header(&token)?;

This does not perform any signature verification or validate the token claims.

You can also decode a token using the public key components of a RSA key in base64 format. The main use-case is for JWK where your public key is in a JSON format like so:

{
   "kty":"RSA",
   "e":"AQAB",
   "kid":"6a7a119f-0876-4f7e-8d0f-bf3ea1391dd8",
   "n":"yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ"
}
// `token` is a struct with 2 fields: `header` and `claims` where `claims` is your own struct.
let token = decode::<Claims>(&token, &DecodingKey::from_rsa_components(jwk["n"], jwk["e"]), &Validation::new(Algorithm::RS256))?;

If your key is in PEM format, it is better performance wise to generate the DecodingKey once in a lazy_static or something similar and reuse it.

Convert SEC1 private key to PKCS8

jsonwebtoken currently only supports PKCS8 format for private EC keys. If your key has BEGIN EC PRIVATE KEY at the top, this is a SEC1 type and can be converted to PKCS8 like so:

openssl pkcs8 -topk8 -nocrypt -in sec1.pem -out pkcs8.pem

Validation

This library automatically validates the exp claim, and nbf is validated if present. You can also validate the sub, iss, and aud but those require setting the expected values in the Validation struct. In the case of aud, if there is a value set in the token but not in the Validation, the token will be rejected.

Validation is only made on present fields in the claims. It is possible to define the required claims, hence verifying that a JWT has a value for each of these claims before it is considered for validation. The required claims can be set in the Validation struct. The default option requires the exp claim to be present.

Since validating time fields is always a bit tricky due to clock skew, you can add some leeway to the iat, exp, and nbf validation by setting the leeway field.

Last but not least, you will need to set the algorithm(s) allowed for this token if you are not using HS256.

Look at examples/validation.rs for a full working example.

jsonwebtoken's People

Contributors

akarys42 avatar alexey-n-chernyshov avatar briansmith avatar brocaar avatar cduvray avatar clehner avatar dowwie avatar fsmaxb avatar giarc3 avatar giraffate avatar hexilee avatar jake-shadle avatar jbg avatar jnicholls avatar keats avatar lpotthast avatar matthauck avatar maxburke avatar mettke avatar mike-engel avatar nickufer avatar oliboy50 avatar olitha avatar p3s avatar ryman avatar sabify avatar ten0 avatar tottoto avatar untitaker avatar wartswerdna avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonwebtoken's Issues

Is jsonwebtoken able to validate Firebase Auth token ?

Hello,
I try to figure out how to validate a jwt issued by Firebase Auth service.
I try with this code with no chance :

let private_key = "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgk******3sPj2xm0C\nl76YeEQy0+BJqSvNq5Z05g==\n-----END PRIVATE KEY-----\n";
let mut validation = Validation::new(Algorithm::RS256);
let header = decode_header(&token).unwrap();
eprintln!("Token  header: {:?}", header);

let token = decode::<Claims>(&token, private_key.as_ref(), &validation);
eprintln!("Token : {:?}", token.unwrap());

And Claim struct:

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    sub: String
}

The header is correctly parsed but the claim cannot be verified (InvalidSignature). Since I'm new to this, maybe I did something wrong, can someone help me please ?

Keys in test folder

Please specify that in the test folder, the correct public key for private_rsa_key.pem is public_rsa_key_8.pem and not public_rsa_key.pem. Looking at the tests where the *.der files are one would assume that it's public_rsa_key.pem that is the correct one but I only got the test token verified by using public_rsa_key_8.pem and thought there was something wrong in my base64 to bytearray conversion :)

Unsafe default behavior

If algorithm is not explicitly enforced via Validation, library will happily accept "forged" token where RS256 algorithm is replaced with HS256 and token is signed with RSA public key used as a secret.

Not sure what could be done here as this isn't the library issue per se, but rather unsafe usage of it. Maybe warning in the documentation?

Alternatively, library could accept the only one algorithm as a parameter. Since key you need to pass to decode is specific to the algorithm anyway, the current signature of (token: &str, key: &[u8], validation: &Validation) is not very useful.

More details

Move to ring

PR welcome, otherwise I'll do it when I have time

Conflict in validation::validate

main.rs

extern crate jsonwebtoken;
#[macro_use]
extern crate serde_derive;
extern crate serde;
use jsonwebtoken::{decode, encode, Header, Validation};

#[derive(Debug, Serialize, Deserialize)]
struct Meta {
    id: i32,
}

fn main() {
    let v = Validation {
        leeway: 5,
        validate_exp: true,
        iss: Some("iss no check".to_string()),
        sub: Some("sub no check".to_string()),
        ..Validation::default()
    };

    let meta = Meta { id: 32 };

    let token = encode(&Header::default(), &meta, "secret".as_ref()).unwrap();

    println!("{:#?}", v);
    println!("{:}", token);

    if let Ok(new_meta) = decode::<Meta>(&token, "secret".as_ref(), &v) {
        println!("{}", "succed");
        println!("{:?}", new_meta);
    } else {
        println!("{}", "failed");
    }
}

output

Validation {
    leeway: 5,
    validate_exp: true,
    validate_iat: true,
    validate_nbf: true,
    aud: None,
    iss: Some(
        "iss no check"
    ),
    sub: Some(
        "sub no check"
    ),
    algorithms: [
        HS256
    ]
}
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MzJ9.BtzaL8AzJJtMOqPWZM2sSmOfTSje5osCILeWGEpWqBA
{
    "id": Number(
        32
    )
}
Validation {
    leeway: 5,
    validate_exp: true,
    validate_iat: true,
    validate_nbf: true,
    aud: None,
    iss: Some(
        "iss no check"
    ),
    sub: Some(
        "sub no check"
    ),
    algorithms: [
        HS256
    ]
}
succed
TokenData { header: Header { typ: Some("JWT"), alg: HS256, cty: None, jku: None, kid: None, x5u: None, x5t: None }, claims: Meta { id: 32 } }

From https://github.com/Keats/jsonwebtoken/blob/master/src/validation.rs#L121-L147

    if let Some(exp) = claims.get("exp") {
        if options.validate_exp && from_value::<i64>(exp.clone())? < now - options.leeway {
            return Err(ErrorKind::ExpiredSignature.into());
        }
    }

    if let Some(nbf) = claims.get("nbf") {
        if options.validate_nbf && from_value::<i64>(nbf.clone())? > now + options.leeway {
            return Err(ErrorKind::ImmatureSignature.into());
        }
    }

    if let Some(iss) = claims.get("iss") {
        if let Some(ref correct_iss) = options.iss {
            if from_value::<String>(iss.clone())? != *correct_iss {
                return Err(ErrorKind::InvalidIssuer.into());
            }
        }
    }

    if let Some(sub) = claims.get("sub") {
        if let Some(ref correct_sub) = options.sub {
            if from_value::<String>(sub.clone())? != *correct_sub {
                return Err(ErrorKind::InvalidSubject.into());
            }
        }
}

Should check options.validate_exp then echeck claims.get("exp"), the others are probably the same.

`decode` doesn't take arbitrary bytes as input

I have a key that is not valid UTF-8. Currently it's possible to encode, but not decode using such a key.

I've been working on a PR that makes decode take AsRef<[u8]> (like encode), but on the way I've found that this breaks decode::<Claims>(..) usage because of additional type parameters. The other options are:

  • decode<T: Part>(token: &str, secret: &AsRef<[u8]>, algorithm: ...) -> Result<T, ...>, no idea why this works???
  • Just advise users to use different forms of typehinting if possible, and only use decode::<Claims, _> if necessary.

Make TokenData struct public

Currently the TokenData struct does not appear to be made public, and thus does not appear in the documentation. To my understanding, this will eventually cause an error during building as well. A top-level pub use should handle this, as you do with Header.

DER from x509 (auth0)

trying to use this with auth0, where its only possible to get the x509 certificate that is used for signing

openssl x509 -in auth0.pem -outform DER -out auth0.der

doesnt seem to work. i'm getting ' Error(InvalidSignature)'.
the same token validates fine on jwt.io with that certificate.

audience and its validation logic require revision

@Keats I think that the Validation type's aud field and the aud validation logic requires reconsideration.

Validation config's aud field

The Validation type should use aud: Option<String>, not Option<Value>. The jwt spec states that aud can be one or more strings, and this remains true for the aud claim within a jwt. However, this rule does not apply to the Validation config's aud field. The Validation aud field is a name (who am I -- a service named "one"). This name is used to check audience membership with the jwt's aud claim.

the aud field of a jwt claim

Suppose a token server creates a subject a new token that is valid for use with server's "one" and "two". One way to accomplish this is by setting the aud claim within the jwt to ["one", "two"].

Consider server "one" registers its name, "one", for the aud field within its Validation config (directly, without the need for set_audience). When it authenticates a request that contains a jwt token where the aud claim is ["one", "two"], server "one" ought to consider this token valid because "one" is a member of the intended audience. Further, server "two" ought to validate the same, assuming it registered "two" within its Validation config. Yet, the current validation logic does not take this into consideration. The current validation logic is all-or-nothing. What I propose instead is a check for audience membership.

The jwt spec seems to require the use of a serde_json::Value type for the claim's aud field so as to consider String or Vec. Consequently, the new aud validation logic will need to convert to compatible types that can then check Validation.aud for membership.

Does this make sense? Do you agree? I can submit a PR for this, if so.

Essentially the following, and open to ideas as to how to optimize this further:

use serde_json::{from_value, to_value, Value};

fn validate_aud_claim(from_config: String, from_jwt: Value) -> Result<(), &'static str>{
    let aud_from_claim: Vec<String> = match from_jwt.is_array() {
        true => from_value(from_jwt).unwrap(),
        false => match from_jwt.is_string() {
                    true => vec![from_jwt.to_string()],
                    false => panic!("invalid aud type") // change to Error..
                }
    };
    
    if aud_from_claim.iter().any(|val| *val == from_config){
        Ok(())
    } else {
        Err("error")
    }
}

fn main() {
    let from_config = "two".to_string();
    let from_claim = to_value(vec!["one", "two"]).unwrap();
    let result = validate_aud_claim(from_config, from_claim);
    dbg!(result);
    
    let from_config = "one".to_string();
    let from_claim = to_value("three").unwrap();
    let result = validate_aud_claim(from_config, from_claim);
    dbg!(result);
    
}

Claim validation assumes all date fields are serialized as `i64`

validation::validate assumes all relevant date fields are serialized as i64 as they are passed to serde_json::from_value::<i64>.
Per default this is not the case for e.g. chrono::DateTime which seems to be the de-facto standard type to represent timestamps.
It took me several hours to realize the error happens in validate as the error message said something along the lines of found string "<valid timestamp with timezone string>". expected i64 which I interpreted as a failure in parsing the claims themselves.
I am not too familiar with serde and chrono internals but it should be possible to deserialize the complete chrono::DateTime and compare this with now obtained earlier in the function.
I am a little busy right now, but might whip up a PR in the next days..

Provide example using chrono::DateTime

chrono is a very popular date and time library and defaults to serializing as ISO strings instead of timestamps as per the JWT spec. It'd be nice to have an example using the #[serde(with = "..")] attribute. I can submit a PR with this example if it is accepted for inclusion.

RS256 RSA decode problem

Hi,

I am looking for some advice with regards to decode and verification of tokens signed with RS256. We seem to be receiving invalid signature errors when passing in the suggested der public key format.

For some back story, We currently have remote systems generating and signing these tokens, the JWT token we need to verify is signed remotely by a nodejs process leveraging this library (https://github.com/auth0/node-jsonwebtoken). Much of the implementation details with regards to signing are fairly typical, but I have some uncertainty with regards to converting and passing the .der format to your library.

For some information on the RSA signing, as well as the public key conversion and some example implementation, I submit the following info.

public / private pem generation (node system)

# generates private.pem
ssh-keygen -t rsa -b 2048 -f private.pem -N ''
# generates public.pem
openssl rsa -in private.pem -pubout -outform PEM -out public.pem

which are then converted to the .der format with the following.

convert public.pem to public.der (for use in rust)

openssl rsa -pubin -in public.pem -outform DER -out public.der

sample code to use the token

fn load_key(filename: &str) -> Vec<u8> {
  let mut buffer = Vec::<u8>::new();
  let mut file   = File::open(filename).unwrap();
  file.read_to_end(&mut buffer).unwrap();
  buffer
}

fn main() {
    let encoded = "<encoded jwt string>";
    // this works fine, suggesting the token is correct.
    let decoded = dangerous_unsafe_decode::<Claims>(&encoded).unwrap();

    // seems to fail with "invalid signature".
    let public_key = load_key("./public.der");
    let validation = Validation::new(Algorithm::RS256);
    let decoded = decode::<Claims>(&encoded, &public_key, &validation).unwrap();
}

Any assistance you could provide would be hugely appreciated.

Regards
S

v7 discussion

There are quite a few changes happening in the PRs: more input format for RSA (#69, #74), ECDSA signing & verification (#73) as well as some Validation changes.

I have already removed the iat check in the next branch since it isn't something that should be checked.

Right now, Validation::algorithms is a vec. I don't remember why but it shouldn't be the case, it should be an algorithm: Algorithm instead, I will accept a PR for that or do it myself later.

#69 also adds a standalone verify_rsa fn, which I'm wondering if we can somehow streamline it with the rest of the crate.

Since Rust doesn't have default parameters, we always have to pass a Validation struct currently. Maybe we can put the decoding onto this struct instead so you get something like:

// currently
let token = decode::<Claims>(&token, "secret".as_ref(), &Validation::default())?;

// possible
// `JwtDecoder` has the same fields as the current `Validation`
let token = JwtDecoder::default().decode_hs256::<Claims>(&token, "secret".as_ref())?;

This way we don't have a single function where we are trying to fit all arguments inside and the user has to select explicitely which decode fn to use. This solves the vec of algorithms at the same time and allows having better documentation for each. The downside is duplication of those functions/docs for the various digest values (decode_hs256, decode_hs384, decode_hs512 for each algo).

Any other ideas/criticisms/things missing?

ccing the people involved in various issues/PRs recently
@AaronFriel @jbg @Jake-Shadle @matthew-nichols-westtel @greglearns

How can I best contribute?

Hey there! First off, thanks for making what's shaping up to be a great crate!

I'm new to Rust, and getting my toes wet by building a jwt cli tool. I'd love to contribute back to this project, but I'm not sure where I would be most helpful. Docs? Some easy feature? I'm not sure what you have in progress already so let me know if I can help.

Update Ring to 0.14

I appreciate the challenge of chasing Ring's versions, but ATM jsonwebtoken can't be used with rustls (which depends on ring 0.14).

Is that process basically mechanical? i.e. Bump Ring, test, bump version, publish?

Switch bak to rust-crypto

No longer can compile

/ring-3628119f6179288e/out/obj/crypto/aes/asm/aes-x86_64.S:3:1: error: unknown directive .type _x86_64_AES_encrypt,@function ^

ring currently requires a lot of host specific build tools, which now makes this package unable to cross compile. At least keep this able to cross compile until ring gets it's build chain setup better.

How to set expiration with examples.

Ok so I am trying to use this crate and I keep getting expired signature when I try and decode my jwt when the client sends it back to me. I have a field in my struct that gets encoded as a jwt called exp of type usize and ive set it to 3600 (1 hour) but it just seems to be ignored. Am I doing something wrong and if so, what is the correct way to set the expiration time of the token?

Nevermind, I realized I had to use now + an hour. ๐Ÿ˜„ closing issue.

Incompatible with Rocket atm

error: multiple packages link to native library `ring-asm`, but a native library can be linked only once

package `ring v0.11.0`
    ... which is depended on by `cookie v0.9.2`
    ... which is depended on by `rocket v0.3.14`
    ... which is depended on by `rocket_contrib v0.3.14`
    ... which is depended on by `xxx v0.1.0 (file:///home/xx/xx)`
links to native library `ring-asm`

package `ring v0.12.1`
    ... which is depended on by `jsonwebtoken v4.0.1`
    ... which is depended on by `xxx v0.1.0 (file:///home/xx/xx)`
also links to native library `ring-asm`

see briansmith/ring#575

I know this is not the responsibility of jsonwebtoken, but this should be left open until resolved since rocket is a well known framework.

v2.0.3 is compatible

.

.

Access token header without obtaining payload?

In order to provide an accurate key for some applications (e.g. OpenID Connect for Google), we need to be able to access the kid (Key ID) field of the header. I could also imagine other fields in the header would be necessary to find the validation context.

The current API allows by decoding a JWT with no signature verification, however I feel that it would be less error-prone if there were a separate function to access the header directly. In that case, you wouldn't have a non-validated payload in the code, and have less chance of accidentally using it without appropriate validation.

The simplest way would be just to add a function decode_header like so:

fn decode_header(token: &str) -> Result<header::Header> ...

How to use JsonWebToken when I don't have a DER and can't use command line tools

I have a rust service that needs to verify JWTs from 3rd-party services that I do not control. The 3rd party service (Portier, in this case) supplies the RSA public components (n modulus, and e exponent). Given that jsonwebtoken can only read DER encoded keys and that I cannot manually run the recommended command-line options for OpenSSL, what is the recommended way of being able to decode a JWT using JsonWebToken in a rust service?

Currently, I have been able to get an ssh public key from the rsa components (n, e) https://github.com/coreos/openssh-keys/blob/master/src/lib.rs#L363 but that seems like a dead end since a public ssh key doesn't seem to be easily converted to DER.

Any ideas?

Split out JWS implementation

I'm thinking of trying to implement the ACME protocol (e.g. LetsEncrypt), which requires JWS. Would you consider splitting that part of the functionality into a separate library or at least making the appropriate parts public?

Expose claims `Map<_, _>` when decoding

I'm making a library that extends jwt and adds some extra validation to the claims, and have run into a problem. Since I'd like the user of my library to define their Claims struct, I don't want to provide the type, but I'd like to perform some validation on it nonetheless.

// My validate function:
pub fn validate<T: DeserializeOwned>(token: &str, key: &[u8], validation: &Validation) -> Result<TokenData<T>, Error> {
  let token_data = decode::<T>(&token, "secret".as_ref(), &Validation::default())?;
  
  // HERE: I need to perform some validation on the claims.
  // Some fields will be marked as mandatory, etc...
  token_data.claims_map.get("iss").expect("No `iss` claim found");

  Ok(token_data)
}

I'd like a way to receive the Map<String, Value> found here so that I can perform some checks on the payload before returning it to the user. Unfortunately, I can't rely on the user's custom T struct since that may not have the claims the library will need to validate.

Could we consider adding another field to the TokenData struct which includes the Map<String, Value> struct as well?

nbf claims does not validate

version: 6.0.1
algorithm: Algorithm::RS512

I just add nbf for 4200 sec from now. After 5-10 sec. i try to validate this token but it is pass (set leeway just 60 sec. but it should not be pass). so i think it not validate nbf time. (My purpose is for validate refresh_token that can use after access_token expired or refresh_token.claims.nbf = access_token.claims.exp )

  • exp is works but nbf not works *

my claims:

{
  "exp": 1567042832,
  "nbf": 1567042232,
  "iat": 1567038032
}

my validation:

  let validation = Validation { 
    leeway: 60,
    algorithms: vec![Algorithm::RS512],
     ..Validation::default() 
  };
  let token_data = match decode::<T>(&token, &PUBLIC_KEY, &validation) {
    Ok(c) => c,
    Err(err) => match *err.kind() {
      ErrorKind::InvalidToken => {
        error!("InvalidToken");
        return None;
      },
      ErrorKind::InvalidAudience => {
        error!("InvalidAudience");
        return None;
      },
      ErrorKind::InvalidIssuer => {
        error!("InvalidIssuer");
        return None;
      },
      ErrorKind::InvalidSignature => {
        error!("InvalidSignature");
        return None;
      },
      ErrorKind::ExpiredSignature => {
        error!("ExpiredSignature");
        return None;
      },
      _ => { 
        error!("TokenError");
        return None;
      }
    },
  };

Upgrade ring to 0.3.0

ring has been upgraded to 0.3.0 and has yanked all previous versions, causing jsonwebtoken to fail to resolve dependencies:

> cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
error: no matching package named `ring` found (required by `jsonwebtoken`)
location searched: registry https://github.com/rust-lang/crates.io-index
version required: ^0.2
versions found: 0.3.0

PEM -> DER public key conversion

Hi, i tried to use this library to secure a micro service but i was unable to verify the signature based on the public key from our auth service. I was converting the public key from the PEM into the DER format but without success. I have read the notice in the README and found a related issue but all this suggested is that i have to create the DER file out of the private key from the auth server. I am not an expert in crypto and i don't really know why this is necessary in your specific case (and why i cannot just build the DER from the public PEM key like openssl rsa -pubin -inform PEM -in public_key.pem -outform DER -out public_key.der) but isn't this a total deal breaker to require the private key in an asymmetric crypto schema?

I am not getting the private key from our auth department to create a public key in DER format to use this library. Am i missing something or is this library just not suitable for my case?

Move to Serde

Not immediately though, wait till it is easily usable on stable (see macros 1.1 stabilisation)

Release new version

Version 1.1.5 no longer works because the ring 0.3.1 dependency fails to compile. The latest master with ring 0.6 compiles successfully. Tested on latest nightly.

ECDSA nistp256 from HEX

I'm struggle to encode using a private key in hexadecimal format.
I need to convert library from another language.

The key in hex are like this one a3ed0dd27cbfa62e13e340fb3dbb86895b99d5fd330a80e799baffcb1d29c17a

I've tried to decode, or even encapsulate with other libraries like signatory but I can't make it work.

jsonwebtoken fails to verify valid token

I have a known valid token, verified with https://jwt.io/. Yet, no matter what I do jsonwebtoken declares InvalidSignature. Here's an example:

fn main() {
  let token = "eyJ0e...available privately on request......hbQQ";

  let pem = "-----BEGIN CERTIFICATE-----
MIIC/TCCAeWgAwIBAgIJIayPWv6q6acCMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNV
BAMTEW51dmVtZnMuYXV0aDAuY29tMB4XDTE5MDEyMTAxMTEyNloXDTMyMDkyOTAx
MTEyNlowHDEaMBgGA1UEAxMRbnV2ZW1mcy5hdXRoMC5jb20wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCvsckLZecpNoAx1nsJRo2h1WyEb2Kc7Guard/j
pxxD7QMjCdOPGtWdoAq2yqKceKGhhojgqFMCBhx/eqeScaAl2Kvf59cl7JWxDVJ+
n9givgQVPy5ZczvimZZMQCwr8F7/IJ2kkTdVR0ytNclVGZqmxzrSiTIev0Hlf09W
JaQipRNI5Yv7yCYv1/P6xXozmdtmO0yjjNEXCY/+T36n8S72XUkX6VfKx09ZkLbT
y9gxyg9X7idnr2Jg93m9RmyO8xay1vQBZxkxM4kKYOlLOGpoulolALfgkf9cR09G
zojQxuBKfROCYVLfHMsN3IiNSs1J40/iPea1r+ZsDiTPrbZdAgMBAAGjQjBAMA8G
A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEBuuLqv76vx3TZ7DIe/Y3pk6MjTMA4G
A1UdDwEB/wQEAwIChDANBgkqhkiG9w0BAQsFAAOCAQEAq3fyyi8Sl92P54gIxOhV
Q5OXet4bhETaW792XgMWhfcG/ppbZwT1krWflDps9BzSfNI7WpPAdreYk+hKSuCu
oNVU0imgha+1SKwxcRbW6Gx9XTn7aTIqP7xxk5uRLO79O5Dhy0d/1YVHpJJK2lKZ
Ridu3W1hbBvXPWgtl4jgYPYmoUcFH/jv0PP4H1ka7rnV6JLtqEBB1naSfYhA3U9s
3VvcS03EPDZIoiNzsiPu4qepVbf/mprxFEztR9HCqHGE+1oBxmt+wjfVRgIDGHcY
rWr2f6rC/AfuqOsPayb4SLQIFujxqJ0nwy3CUrlAlfQvYOnrkMQoccj2nklY86kl
Gw==
-----END CERTIFICATE-----";

  let der = openssl::x509::X509::from_pem(pem.as_bytes())
    .unwrap()
    .to_der()
    .unwrap();

  let decoded = jsonwebtoken::decode::<AccessToken>(
    token,
    &der,
    &jsonwebtoken::Validation {
      algorithms: vec![jsonwebtoken::Algorithm::RS256],
      ..Default::default()
    },
  );

  println!("{:?}", decoded)
}

No matter what jsonwebtoken returns Err(Error(InvalidSignature)). The payload looks like:

{
  "https://hasura.io/jwt/claims": {
    "x-hasura-default-role": "user",
    "x-hasura-allowed-roles": [
      "user"
    ],
    "x-hasura-user-id": "......."
  },
  "iss": "https://nuvemfs.auth0.com/",
  "sub": "google-oauth2|.......",
  "aud": [
    "https://nuvenmfs/api",
    "https://nuvemfs.auth0.com/userinfo"
  ],
  "iat": 1552895641,
  "exp": 1552982041,
  "azp": "......",
  "scope": "openid profile email offline_access"
}

Decoding with x509 certs

I'm having a hard time authenticating a token using a x5c. (MS OAuth/Azure)

Below is the code...

// Trying to isolate the problem by only checking the signature. 
let validation_config = jsonwebtoken::Validation {
            algorithms: vec![jsonwebtoken::Algorithm::RS256],
            leeway: 0,
            validate_exp: false,
            validate_iat: false,
            validate_nbf: false,
            aud: None,
            iss: None,
            sub: None
        };
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIn...";
let x5c_cert = "MIIDBTCCAe2gAwIBAgIQKOfEJNDyDplBSXKYcM..."; 

let raw_der = base64::decode_config(der, base64::STANDARD).unwrap();
let d = jsonwebtoken::decode::<MsOAuthPayload>(&token, &raw_der, &validation_config);

The above always returns InvalidSignature.

  • RS265 is the correct algo.
  • The cert is correct. I tried it on jwt.io by adding a BEGIN/END cert and it validates fine.
  • I used ssl to convert the BEGIN/END pem to DER and the bytes match up from the base 64 decode.
  • The key URL is: https://login.microsoftonline.com/common/discovery/v2.0/keys but my specific tenant returns the same keys.

Anyone have some insight on what I'm doing wrong here?

Thanks

Bump untrusted version for security

Hi,

This project seems to use untrusted = 0.5 as I can see from the cargo.toml file on master branch. Recently a security issue was fixed as part of 0.6.2 release. Please refer to rustsec/advisory-db@3c0458d .

Kindly ignore if this is irrelevant or fixed on another branch.

Thanks.

Add a way to get the header + payload without verifying the signature

Hey! I've gotten a new issue on my cli tool (mike-engel/jwt-cli#4) which was fixed in 3.0 with typ being optional. 3.0 did, however, break one use case of my tool which is to just see the contents of a JWT without verifying the signature (debugging, etc). I used to use the validate_signature option in Validation, which was removed in 3.0.

Would you be open to having a separate method to just decode a token without verification? I looked through a couple of issues and didn't see the reason for removing validate_signature, but I'm sure there was one.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.