[dependencies]jsonwebtoken = { version = "=11.1.0", features = ["rust_crypto"] } # or "aws_lc_rs"serde = { version = "1", features = ["derive"] } use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode};use serde::Deserialize; #[derive(Debug, Deserialize)]struct Claims { sub: String, exp: u64,} fn main() -> Result<(), Box<dyn std::error::Error>> { let token = std::fs::read_to_string("token.txt")?; let key = DecodingKey::from_ec_pem(&std::fs::read("pub.pem")?)?; // SPKI "BEGIN PUBLIC KEY" let mut validation = Validation::new(Algorithm::ES256); // the only algorithm accepted validation.set_issuer(&["https://ecdsa.com"]); // checked only if the token has an iss… validation.set_required_spec_claims(&["exp", "iss", "sub"]); // …so require it as well validation.validate_nbf = true; // off by default validation.leeway = 30; // seconds of clock skew; the default is 60 let data = decode::<Claims>(token.trim(), &key, &validation)?; println!("valid ES256 token for {}, exp {}", data.claims.sub, data.claims.exp); Ok(())} How it works
DecodingKey::from_ec_pemreads the SPKIBEGIN PUBLIC KEYblock; for keys from a JWKS endpoint useDecodingKey::from_jwkinstead.Validation::new(Algorithm::ES256)accepts that one algorithm only: a token whose header saysHS256fails withInvalidAlgorithmbefore any signature work.decode::<Claims>verifies the 64-byter‖ssignature over theheader.payloadtext, then deserializes the payload into your struct — a missing or mistyped field is an error, not a silent default.- Errors carry a kind: the run produces
InvalidSignature,InvalidAlgorithm,ExpiredSignatureandMissingRequiredClaim("iss")for a swapped payload, a relabelled header, an expired token and a token without an issuer.
Gotchas
set_issuer(andset_audience) only compare the claim when the token has one. A token withoutisspasses unless"iss"is inset_required_spec_claims— the default requires onlyexp.nbfis not checked unless you setvalidate_nbf = true, andexpgets 60 seconds of leeway by default.validate_audis on by default: a token that carries anaudclaim fails withInvalidAudienceuntil you callset_audiencewith an expected value.- Without exactly one of the
rust_cryptooraws_lc_rsfeatures, jsonwebtoken 11 compiles but panics at the first verification withCould not automatically determine the process-level CryptoProvider. jsonwebtoken::dangerous::insecure_decodeandinsecure_decode_claimsskip the signature and all claim checks. They exist for debugging; nothing read through them may be trusted.