8 unstable releases (3 breaking)

new 0.5.0 Apr 16, 2024
0.4.0 Jul 18, 2023
0.3.0 Jul 17, 2023
0.2.4 Jan 29, 2022
0.1.0 Dec 20, 2019

#569 in Web programming

41 downloads per month

Apache-2.0

36KB
594 lines

jwt-rust

jwts jwts jwts jwts

jwt

A rust implementation of JSON Web Tokens.

Examples

Encode

use jwts::{Claims, jws};
use jwts::jws::Header;
use jwts::jws::alg::HS256;

let claims = Claims {
    iss: Some("sea".to_owned()),
    ..Default::default()
};
jws::encode::<HS256>(Header::default(), &claims, b"secret").unwrap();

Decode

use jwts::{Claims, jws};
use jwts::jws::{Header, NoVerify, Token, VerifyWith};
use jwts::jws::alg::HS256;

let claims = Claims::default();
let token = jws::encode::<HS256>(Header::default(), &claims, b"secret").unwrap();

let Token {..} = jws::decode::<Claims>(&token, NoVerify).unwrap(); // no verify
let Token {..} = jws::decode::<Claims>(&token, VerifyWith::<HS256>(b"secret")).unwrap(); // verify with algorithm and key

Validate Claims

use std::collections::HashMap;
use std::time::{Duration, SystemTime};
use jwts::Claims;
use jwts::validate::{ExpectAud, ExpectIss, ExpectJti, ExpectSub, ExpiredTime, IssuedAtTime, NotBeforeTime, Validate};

let claims = Claims {
    iss: Some("sea".to_owned()),
    sub: Some("subject".to_owned()),
    aud: Some("audience".to_owned()),
    jti: Some("id".to_owned()),
    ..Default::default()
};
let claims = claims
    .issued_now()
    .expired_in(Duration::from_secs(1))
    .not_before(SystemTime::now());

claims.validate(IssuedAtTime).unwrap();
claims.validate(NotBeforeTime).unwrap();
claims.validate(ExpiredTime).unwrap();
claims.validate(ExpectIss("sea")).unwrap();
claims.validate(ExpectSub("subject")).unwrap();
claims.validate(ExpectAud("audience")).unwrap();
claims.validate(ExpectJti("id")).unwrap();

// builtin validation works with any `Serialize` type:
let claims = HashMap::from([("iss", "sea")]);
claims.validate(ExpectIss("sea")).unwrap();

Custom Claims Type

use std::collections::HashMap;
use serde_derive::{Deserialize, Serialize};
use jwts::jws;
use jwts::jws::{Header, Token, VerifyWith};
use jwts::jws::alg::HS256;

#[derive(Debug, Serialize, Deserialize)]
struct CustomClaims {
    iss: String,
}

let claims = CustomClaims {
    iss: "sea".to_owned(),
};
let token = jws::encode::<HS256>(Header::default(), &claims, b"secret").unwrap();
let Token {..} = jws::decode::<CustomClaims>(&token, VerifyWith::<HS256>(b"secret")).unwrap();

// Or use a map directly
let claims = HashMap::from([("iss", "sea")]);
let Token {..} = jws::decode::<HashMap<String, String>>(&token, VerifyWith::<HS256>(b"secret")).unwrap();

Custom Algorithm

use jwts::{Claims, Error, jws};
use jwts::jws::{Algorithm, Header, Token, VerifyWith};

pub struct None;

impl Algorithm for None {
    type SignKey = ();
    type VerifyKey = ();

    fn name() -> &'static str {
        "None"
    }

    fn sign(data: impl AsRef<[u8]>, key: &Self::SignKey) -> Result<Vec<u8>, Error> {
        Ok([].into())
    }

    fn verify(data: impl AsRef<[u8]>, sig: impl AsRef<[u8]>, key: &Self::VerifyKey) -> Result<(), Error> {
        sig.as_ref().is_empty().then_some(()).ok_or(Error::InvalidSignature)
    }
}

let claims = Claims::default();
let token = jws::encode::<None>(Header::default(), &claims, &()).unwrap();
let Token {..} = jws::decode::<Claims>(&token, VerifyWith::<None>(&())).unwrap();

Custom Verification

use jwts::{Claims, Error, jws};
use jwts::jws::{Algorithm, Header, Token, Verify};
use jwts::jws::alg::HS256;

pub struct CustomVerify;

impl Verify<Claims> for CustomVerify {
    fn verify(&self, f2s: &str, signature: &[u8], header: &Header, payload: &Claims) -> Result<(), Error> {
        HS256::verify(f2s, signature, b"secret")
    }
}

let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzZWEifQ.L0DLtDjydcSK-c0gTyOYbmUQ_LUCZzqAGCINn2OLhFs";
let Token {..} = jws::decode::<Claims>(&token, CustomVerify).unwrap();

Custom Claims Validation

use jwts::Claims;
use jwts::validate::{Validate, Validation};

pub struct CustomValidation;

impl Validation<Claims> for CustomValidation {
    type Error = ();

    fn validate(&self, claims: &Claims) -> Result<(), Self::Error> {
        claims.aud.is_some().then_some(()).ok_or(())
    }
}

let claims = Claims {
    aud: Some("audience".to_owned()),
    ..Default::default()
};
claims.validate(CustomValidation).unwrap();

Algorithms

Sign and verify using crate aws-lc-rs.

  • HS256 - HMAC using SHA-256
  • HS384 - HMAC using SHA-384
  • HS512 - HMAC using SHA-512
  • RS256 - RSASSA-PKCS1-v1_5 using SHA-256
  • RS384 - RSASSA-PKCS1-v1_5 using SHA-384
  • RS512 - RSASSA-PKCS1-v1_5 using SHA-512
  • ES256 - ECDSA using P-256 and SHA-256
  • ES384 - ECDSA using P-384 and SHA-384
  • ES512 - ECDSA using P-521 and SHA-512
  • PS256 - RSASSA-PSS using SHA-256 and MGF1 with SHA-256
  • PS384 - RSASSA-PSS using SHA-384 and MGF1 with SHA-384
  • PS512 - RSASSA-PSS using SHA-512 and MGF1 with SHA-512

Migrate from 0.2

<= 0.2 >= 0.4
Token::sign jws::encode
Token::decode jws::decode with NoVerify
Token::verify_with_key jws::decode with VerifyWith
Token::verify_with_key_resolver jws::decode with custom verify
Token::validate_claims Validate::validate

More

RFC 7519 JSON Web Token (JWT)

RFC 7515 JSON Web Signature (JWS)

RFC 7518 JSON Web Algorithms (JWA)

License

Apache 2.0 License

Dependencies

~50MB
~1.5M SLoC