[dependencies]# jsonwebtoken 10+ needs a crypto backend: "rust_crypto" (pure Rust) or "aws_lc_rs"jsonwebtoken = { version = "=11.1.0", features = ["rust_crypto"] }serde = { version = "=1.0.229", features = ["derive"] }use std::time::{SystemTime, UNIX_EPOCH}; use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};use serde::Serialize; #[derive(Serialize)]struct Claims<'a> { iss: &'a str, sub: &'a str, aud: &'a str, iat: u64, exp: u64,} fn main() -> Result<(), Box<dyn std::error::Error>> { // from_ec_pem wants PKCS#8 ("BEGIN PRIVATE KEY") let key = EncodingKey::from_ec_pem(&std::fs::read("key.pem")?)?; let mut header = Header::new(Algorithm::ES256); header.kid = Some("2026-10".to_owned()); let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); let claims = Claims { iss: "https://issuer.example.com", sub: "user-42", aud: "api://orders", iat: now, exp: now + 15 * 60, }; println!("{}", encode(&header, &claims, &key)?); Ok(())}How it works
EncodingKey::from_ec_pemreads aBEGIN PRIVATE KEY(PKCS#8) block.Header::new(Algorithm::ES256)setsalgandtyp;header.kidadds the key id.- The claims are any
Serializetype;iatandexpareu64seconds fromSystemTime. encodereturns the compact token as aString.
Gotchas
- Without the
rust_cryptooraws_lc_rsfeature, the code still compiles and then panics at the first signature:Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features. from_ec_pemrejects SEC 1 keys (BEGIN EC PRIVATE KEY) withError(InvalidKeyFormat). Convert them withopenssl pkcs8 -topk8 -nocrypt -in ec.pem -out key.pem.- Choose exactly one backend: with both features enabled, jsonwebtoken cannot pick a provider automatically and you must install one with
CryptoProvider::install_default(). - Use a struct for claims rather than
serde_json::Valuewith ad-hoc fields: the types catch a stringexpor a missingaudat compile time.