jwt_simple/lib.rs
1//! [](https://github.com/jedisct1/rust-jwt-simple/actions)
2//! [](https://docs.rs/jwt-simple/)
3//! [](https://crates.io/crates/jwt-simple)
4//!
5//! <!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
6//!
7//! <!-- code_chunk_output -->
8//!
9//! - [JWT-Simple](#jwt-simple)
10//! - [Usage](#usage)
11//! - [Authentication (symmetric, `HS*` JWT algorithms) example](#authentication-symmetric-hs-jwt-algorithms-example)
12//! - [Keys and tokens creation](#keys-and-tokens-creation)
13//! - [Token verification](#token-verification)
14//! - [Signatures (asymmetric, `RS*`, `PS*`, `ES*`, `EdDSA` and `ML-DSA` algorithms) example](#signatures-asymmetric-rs-ps-es-eddsa-and-ml-dsa-algorithms-example)
15//! - [Key pairs and tokens creation](#key-pairs-and-tokens-creation)
16//! - [ES256](#es256)
17//! - [ES384](#es384)
18//! - [ML-DSA](#ml-dsa)
19//! - [JWE (Encrypted tokens)](#jwe-encrypted-tokens)
20//! - [RSA-OAEP key management](#rsa-oaep-key-management)
21//! - [AES Key Wrap](#aes-key-wrap)
22//! - [ECDH-ES key agreement](#ecdh-es-key-agreement)
23//! - [ECDH-ES key agreement with X25519](#ecdh-es-key-agreement-with-x25519)
24//! - [JSON Web Keys (JWK)](#json-web-keys-jwk)
25//! - [Strict JWK validation](#strict-jwk-validation)
26//! - [JWK thumbprints](#jwk-thumbprints)
27//! - [Advanced usage](#advanced-usage)
28//! - [Custom claims](#custom-claims)
29//! - [Peeking at metadata before verification](#peeking-at-metadata-before-verification)
30//! - [Creating and attaching key identifiers](#creating-and-attaching-key-identifiers)
31//! - [Mitigations against replay attacks](#mitigations-against-replay-attacks)
32//! - [Salted keys](#salted-keys)
33//! - [CWT (CBOR) support](#cwt-cbor-support)
34//! - [Specifying header options](#specifying-header-options)
35//! - [Validating content and signature types](#validating-content-and-signature-types)
36//! - [Working around compilation issues with the `boring` crate](#working-around-compilation-issues-with-the-boring-crate)
37//! - [Usage in Web browsers](#usage-in-web-browsers)
38//! - [Why yet another JWT crate](#why-yet-another-jwt-crate)
39//!
40//! <!-- /code_chunk_output -->
41//!
42//! # JWT-Simple
43//!
44//! A new JWT (JSON Web Tokens) implementation for Rust that focuses on simplicity, while avoiding common JWT security pitfalls.
45//!
46//! `jwt-simple` is unopinionated and supports all commonly deployed authentication and signature algorithms:
47//!
48//! | JWT algorithm name | Description |
49//! | ------------------ | ------------------------------------- |
50//! | `HS256` | HMAC-SHA-256 |
51//! | `HS384` | HMAC-SHA-384 |
52//! | `HS512` | HMAC-SHA-512 |
53//! | `BLAKE2B` | BLAKE2B-256 |
54//! | `RS256` | RSA with PKCS#1v1.5 padding / SHA-256 |
55//! | `RS384` | RSA with PKCS#1v1.5 padding / SHA-384 |
56//! | `RS512` | RSA with PKCS#1v1.5 padding / SHA-512 |
57//! | `PS256` | RSA with PSS padding / SHA-256 |
58//! | `PS384` | RSA with PSS padding / SHA-384 |
59//! | `PS512` | RSA with PSS padding / SHA-512 |
60//! | `ES256` | ECDSA over p256 / SHA-256 |
61//! | `ES384` | ECDSA over p384 / SHA-384 |
62//! | `ES256K` | ECDSA over secp256k1 / SHA-256 |
63//! | `EdDSA` | Ed25519 |
64//! | `ML-DSA-44` | ML-DSA-44 (FIPS 204, post-quantum) |
65//! | `ML-DSA-65` | ML-DSA-65 (FIPS 204, post-quantum) |
66//! | `ML-DSA-87` | ML-DSA-87 (FIPS 204, post-quantum) |
67//!
68//! JWE (JSON Web Encryption) is also supported with the following key management algorithms:
69//!
70//! | JWE algorithm name | Description |
71//! | ------------------ | -------------------------------------------- |
72//! | `RSA-OAEP` | RSA with OAEP using SHA-1 (not recommended) |
73//! | `A256KW` | AES-256 Key Wrap |
74//! | `A128KW` | AES-128 Key Wrap |
75//! | `ECDH-ES+A256KW` | ECDH (P-256 or X25519) with AES-256 Key Wrap |
76//! | `ECDH-ES+A128KW` | ECDH (P-256 or X25519) with AES-128 Key Wrap |
77//!
78//! Content encryption uses AES-GCM (A256GCM or A128GCM).
79//!
80//! `jwt-simple` can be compiled out of the box to WebAssembly/WASI. It is fully compatible with Fastly _Compute_ service.
81//!
82//! Important: JWT's purpose is to verify that data has been created by a party knowing a secret key. It does not provide any kind of confidentiality: JWT data is simply encoded as Base64, and is not encrypted.
83//!
84//! ## Usage
85//!
86//! `cargo.toml`:
87//!
88//! ```toml
89//! [dependencies]
90//! jwt-simple = "0.13"
91//! ```
92//!
93//! Rust:
94//!
95//! ```rust
96//! use jwt_simple::prelude::*;
97//! ```
98//!
99//! Errors are returned as `jwt_simple::Error` values (alias for the `Error` type of the `anyhow` crate).
100//!
101//! ## Authentication (symmetric, `HS*` JWT algorithms) example
102//!
103//! Authentication schemes use the same key for creating and verifying tokens. In other words, both parties need to ultimately trust each other, or else the verifier could also create arbitrary tokens.
104//!
105//! ### Keys and tokens creation
106//!
107//! Key creation:
108//!
109//! ```rust
110//! use jwt_simple::prelude::*;
111//!
112//! // create a new key for the `HS256` JWT algorithm
113//! let key = HS256Key::generate();
114//! ```
115//!
116//! A key can be exported as bytes with `key.to_bytes()`, and restored with `HS256Key::from_bytes()`.
117//!
118//! Token creation:
119//!
120//! ```rust,ignore
121//! // create claims valid for 2 hours
122//! let claims = Claims::create(Duration::from_hours(2));
123//! let token = key.authenticate(claims)?;
124//! ```
125//!
126//! -> Done!
127//!
128//! ### Token verification
129//!
130//! ```rust,ignore
131//! let claims = key.verify_token::<NoCustomClaims>(&token, None)?;
132//! ```
133//!
134//! -> Done! No additional steps required.
135//!
136//! Key expiration, start time, authentication tags, etc. are automatically verified. The function fails with `JWTError::InvalidAuthenticationTag` if the authentication tag is invalid for the given key.
137//!
138//! The full set of claims can be inspected in the `claims` object if necessary. `NoCustomClaims` means that only the standard set of claims is used by the application, but application-defined claims can also be supported.
139//!
140//! Extra verification steps can optionally be enabled via the `VerificationOptions` structure:
141//!
142//! ```rust,ignore
143//! let mut options = VerificationOptions::default();
144//! // Accept tokens that will only be valid in the future
145//! options.accept_future = true;
146//! // Accept tokens even if they have expired up to 15 minutes after the deadline,
147//! // and/or they will be valid within 15 minutes.
148//! // Note that 15 minutes is the default, since it is very common for clocks to be slightly off.
149//! options.time_tolerance = Some(Duration::from_mins(15));
150//! // Reject tokens if they were issued more than 1 hour ago
151//! options.max_validity = Some(Duration::from_hours(1));
152//! // Reject tokens if they don't include an issuer from that set
153//! options.allowed_issuers = Some(HashSet::from_strings(&["example app"]));
154//!
155//! // see the documentation for the full list of available options
156//!
157//! let claims = key.verify_token::<NoCustomClaims>(&token, Some(options))?;
158//! ```
159//!
160//! Note that `allowed_issuers` and `allowed_audiences` are not strings, but sets of strings (using the `HashSet` type from the Rust standard library), as the application can allow multiple values.
161//!
162//! ## Signatures (asymmetric, `RS*`, `PS*`, `ES*`, `EdDSA` and `ML-DSA` algorithms) example
163//!
164//! A signature requires a key pair: a secret key used to create tokens, and a public key, that can only verify them.
165//!
166//! Always use a signature scheme if both parties do not ultimately trust each other, such as tokens exchanged between clients and API providers.
167//!
168//! ### Key pairs and tokens creation
169//!
170//! Key creation:
171//!
172//! #### ES256
173//!
174//! ```rust
175//! use jwt_simple::prelude::*;
176//!
177//! // create a new key pair for the `ES256` JWT algorithm
178//! let key_pair = ES256KeyPair::generate();
179//!
180//! // a public key can be extracted from a key pair:
181//! let public_key = key_pair.public_key();
182//! ```
183//!
184//! #### ES384
185//!
186//! ```rust
187//! use jwt_simple::prelude::*;
188//!
189//! // create a new key pair for the `ES384` JWT algorithm
190//! let key_pair = ES384KeyPair::generate();
191//!
192//! // a public key can be extracted from a key pair:
193//! let public_key = key_pair.public_key();
194//! ```
195//!
196//! #### ML-DSA
197//!
198//! ML-DSA is a post-quantum signature scheme (FIPS 204). Signing and verification are fast, but public keys and signatures are much larger than their classical counterparts.
199//!
200//! ```rust
201//! use jwt_simple::prelude::*;
202//!
203//! // create a new key pair for the `ML-DSA-44` JWT algorithm
204//! let key_pair = MLDSA44KeyPair::generate();
205//!
206//! // a public key can be extracted from a key pair:
207//! let public_key = key_pair.public_key();
208//! ```
209//!
210//! `MLDSA65KeyPair` and `MLDSA87KeyPair` are also available, for the `ML-DSA-65` and `ML-DSA-87` algorithms. ML-DSA-44 is the recommended choice: its security level is good enough for all practical purposes, and it is much faster than the other variants. A key pair is serialized as its 32-byte seed (`to_bytes()`/`from_bytes()`), and a public key as its raw byte representation.
211//!
212//! Keys can be exported as bytes for later reuse, and imported from bytes or, for RSA, from individual parameters, DER-encoded data or PEM-encoded data.
213//! Every asymmetric key can also be imported and exported as a
214//! [JSON Web Key](#json-web-keys-jwk).
215//!
216//! RSA key pair creation, using OpenSSL and PEM importation of the secret key:
217//!
218//! ```sh
219//! openssl genrsa -out private.pem 2048
220//! openssl rsa -in private.pem -outform PEM -pubout -out public.pem
221//! ```
222//!
223//! ```rust,ignore
224//! let key_pair = RS384KeyPair::from_pem(private_pem_file_content)?;
225//! let public_key = RS384PublicKey::from_pem(public_pem_file_content)?;
226//! ```
227//!
228//! RSA keys must have a modulus of 2048 to 4096 bits, and a public exponent of 65537.
229//! This applies to every format, and to RSA-OAEP keys as well.
230//! There is no option to accept keys outside these bounds.
231//!
232//! Smaller moduli fail with `JWTError::WeakKey`, and other exponents with
233//! `JWTError::InvalidPublicKey` or `JWTError::InvalidKeyPair`.
234//! Larger moduli fail with `JWTError::UnsupportedRSAModulus`, except DER and PEM public
235//! keys with the pure-Rust backend, whose parser rejects them with its own error.
236//!
237//! A few providers still publish weaker keys.
238//! For example, the [Yahoo key set](https://api.login.yahoo.com/openid/v1/certs) only
239//! served 1024-bit RSA keys when it was checked on 2026-09-25.
240//! Loading these keys fails with `WeakKey`, so tokens from Sign in with Yahoo can't be
241//! verified.
242//!
243//! Key sets rotate, so check the keys that are currently published rather than relying
244//! on this example.
245//!
246//! Ed25519 public keys that are not canonically encoded, or that are points of small
247//! order, are rejected by every constructor.
248//! Ed25519 key pairs imported from raw bytes must contain the public key that their
249//! seed derives.
250//!
251//! Token creation and verification work the same way as with `HS*` algorithms, except that tokens are created with a key pair, and verified using the corresponding public key.
252//!
253//! Token creation:
254//!
255//! ```rust,ignore
256//! // create claims valid for 2 hours
257//! let claims = Claims::create(Duration::from_hours(2));
258//! let token = key_pair.sign(claims)?;
259//! ```
260//!
261//! Token verification:
262//!
263//! ```rust,ignore
264//! let claims = public_key.verify_token::<NoCustomClaims>(&token, None)?;
265//! ```
266//!
267//! Available verification options are identical to the ones used with symmetric algorithms.
268//!
269//! ## JWE (Encrypted tokens)
270//!
271//! While JWT signatures provide authenticity (verifying who created the token), JWE provides confidentiality by encrypting the token content. Use JWE when the claims contain sensitive data that should not be visible to intermediaries.
272//!
273//! ### RSA-OAEP key management
274//!
275//! RSA-OAEP uses asymmetric encryption: anyone with the public key can encrypt tokens, but only the private key holder can decrypt them.
276//!
277//! ```rust,ignore
278//! use jwt_simple::prelude::*;
279//!
280//! // Generate a key pair (2048 bits minimum, 4096 recommended for high security)
281//! let decryption_key = RsaOaepDecryptionKey::generate(2048)?;
282//! let encryption_key = decryption_key.encryption_key();
283//!
284//! // Encrypt a token
285//! let claims = Claims::create(Duration::from_hours(1))
286//! .with_subject("user@example.com");
287//! let token = encryption_key.encrypt(claims)?;
288//!
289//! // Decrypt the token
290//! let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None)?;
291//! ```
292//!
293//! Keys can be exported and imported using PEM or DER formats, similar to RSA signature keys.
294//!
295//! `RSA-OAEP` uses SHA-1 inside OAEP, so tokens work with other
296//! JOSE implementations.
297//!
298//! ### AES Key Wrap
299//!
300//! For symmetric encryption where the same key is used for both encryption and decryption:
301//!
302//! ```rust,ignore
303//! use jwt_simple::prelude::*;
304//!
305//! // Generate a 256-bit key
306//! let key = A256KWKey::generate();
307//!
308//! // Or create from existing bytes
309//! let key = A256KWKey::from_bytes(&raw_key_bytes)?;
310//!
311//! // Encrypt
312//! let claims = Claims::create(Duration::from_hours(1));
313//! let token = key.encrypt(claims)?;
314//!
315//! // Decrypt
316//! let claims: JWTClaims<NoCustomClaims> = key.decrypt_token(&token, None)?;
317//! ```
318//!
319//! `A128KWKey` is also available for 128-bit keys.
320//!
321//! ### ECDH-ES key agreement
322//!
323//! ECDH-ES uses elliptic curve Diffie-Hellman for key agreement. Like RSA-OAEP, it uses asymmetric keys but is more efficient:
324//!
325//! ```rust,ignore
326//! use jwt_simple::prelude::*;
327//!
328//! // Generate a key pair
329//! let decryption_key = EcdhEsA256KWDecryptionKey::generate();
330//! let encryption_key = decryption_key.encryption_key();
331//!
332//! // Encrypt
333//! let claims = Claims::create(Duration::from_hours(1));
334//! let token = encryption_key.encrypt(claims)?;
335//!
336//! // Decrypt
337//! let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None)?;
338//! ```
339//!
340//! When a received token includes `apu` and `apv`, they are used to derive the key.
341//! They must be valid base64url, and different from each other when both are present.
342//! Tokens created by `jwt-simple` don't include them.
343//!
344//! ### ECDH-ES key agreement with X25519
345//!
346//! The same key agreement modes work with X25519 keys, which are smaller and faster
347//! than P-256 keys:
348//!
349//! ```rust,ignore
350//! use jwt_simple::prelude::*;
351//!
352//! let decryption_key = X25519EcdhEsA256KWDecryptionKey::generate();
353//! let encryption_key = decryption_key.encryption_key();
354//!
355//! let claims = Claims::create(Duration::from_hours(1));
356//! let token = encryption_key.encrypt(claims)?;
357//!
358//! let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None)?;
359//! ```
360//!
361//! `X25519EcdhEsA128KWDecryptionKey` and `X25519EcdhEsA128KWEncryptionKey` implement
362//! `ECDH-ES+A128KW`.
363//! Keys can be imported and exported as raw bytes, DER, PEM or JWK.
364//!
365//! Public keys must be canonically encoded (top bit clear, value below 2^255 - 19), and
366//! points of small order are rejected.
367//! The same checks apply to the ephemeral key of every received token.
368//!
369//! JWE tokens support the same claim types and custom claims as JWT signatures. Decryption options allow validating claims, requiring specific key IDs, and limiting token size.
370//!
371//! ## JSON Web Keys (JWK)
372//!
373//! Every asymmetric key type supports JSON Web Key import and export,
374//! and can compute its JWK thumbprint:
375//!
376//! ```rust
377//! use jwt_simple::prelude::*;
378//!
379//! let key_pair = ES256KeyPair::generate().with_key_id("2026-09");
380//!
381//! // Publish the public key, and load it on the verifier side.
382//! let public_jwk = key_pair.public_key().to_jwk();
383//! let public_key = ES256PublicKey::from_jwk(&public_jwk)?;
384//! let token = key_pair.sign(Claims::create(Duration::from_hours(1)))?;
385//! public_key.verify_token::<NoCustomClaims>(&token, None)?;
386//!
387//! // Keep this private.
388//! let private_jwk = key_pair.to_jwk();
389//! let key_pair = ES256KeyPair::from_jwk(&private_jwk)?;
390//! assert_eq!(key_pair.jwk_thumbprint(), public_key.jwk_thumbprint());
391//!
392//! # Ok::<(), jwt_simple::Error>(())
393//! ```
394//!
395//! The same `from_jwk()`, `to_jwk()` and `jwk_thumbprint()` methods exist on the
396//! Ed25519, ES256, ES384, ES256K, RS*, PS* and ML-DSA key types, and on the RSA-OAEP,
397//! ECDH-ES and X25519 JWE keys.
398//! Symmetric keys have no JWK support.
399//!
400//! `to_jwk()` on a key pair or a decryption key exports the private key.
401//! Treat its output as the secret it is, and use `public_key().to_jwk()` or
402//! `encryption_key().to_jwk()` to share a key.
403//!
404//! `jwt-simple` wipes its own temporary copies of private keys when it can, but the
405//! application is responsible for the strings and bytes it gets back.
406//!
407//! A JWK is only accepted by the key type it describes.
408//! There is no generic key type that picks an algorithm from the `alg` member of a key.
409//! An application that receives a key set picks the entry it trusts, usually by `kid`,
410//! and passes its JSON to the key type it expects.
411//!
412//! Exported keys include `alg`, `use` (`sig` for signature keys, `enc` for encryption
413//! keys) and, if one is set, the key identifier as `kid`.
414//! This keeps other implementations from using a key for something else.
415//!
416//! Ed25519 keys are exported with `alg` set to `EdDSA`, the name deployed verifiers
417//! expect.
418//! `Ed25519` is accepted on import as well, and Ed25519 public keys verify
419//! tokens using either name.
420//!
421//! ### Strict JWK validation
422//!
423//! Imports are strict, and there is no way to relax them:
424//!
425//! - Documents larger than 8192 bytes (`MAX_JWK_LENGTH`) are rejected before being
426//! parsed, whitespace and unknown members included.
427//! A complete 4096-bit RSA private key takes about 3.2 KB.
428//!
429//! - A document must be a single JSON object without duplicate member names.
430//! Known members must have the right type, and are never allowed to be `null`.
431//!
432//! - Key material must be canonical, unpadded base64url, with the exact size the key
433//! type requires.
434//!
435//! - Members that belong to other key types are rejected, and a public key can't
436//! include any private member, not even an empty one.
437//!
438//! - A private key must be complete, and its public members must be the ones its
439//! private part derives.
440//!
441//! - `alg`, `use` and `key_ops` are optional, but must match the key type when present.
442//! `key_ops` can only list operations the key performs, private keys must list at
443//! least one, and signing keys must include `sign`.
444//! This metadata can only narrow what a key is used for.
445//! It can't turn an ES256 key into an ECDH key, or an RS256 key into a PS256 or
446//! RSA-OAEP key.
447//!
448//! - A `kid` becomes the key identifier.
449//! Other members, such as `ext` or `x5c`, are ignored.
450//!
451//! - Keys go through the same checks as in other formats.
452//! Invalid or small-order Ed25519 and X25519 points, EC points that are not on the
453//! curve, and RSA keys outside the size and exponent policy are rejected.
454//!
455//! | Key types | `kty`, `crv` | Public members | Private members |
456//! | -------------------- | --------------------------------- | -------------- | ------------------------------- |
457//! | Ed25519 | `OKP`, `Ed25519` | `x` | `d`, the 32-byte seed |
458//! | X25519 | `OKP`, `X25519` | `x` | `d` |
459//! | ES256, ES384, ES256K | `EC`, `P-256`/`P-384`/`secp256k1` | `x`, `y` | `d` |
460//! | ECDH-ES | `EC`, `P-256` | `x`, `y` | `d` |
461//! | RS\*, PS\*, RSA-OAEP | `RSA` | `n`, `e` | `d`, `p`, `q`, `dp`, `dq`, `qi` |
462//! | ML-DSA | `AKP` (`alg` is required) | `pub` | `priv`, the 32-byte seed |
463//!
464//! EC coordinates keep their leading zeros, and each coordinate is checked on its own.
465//!
466//! Java exports of RSA keys sometimes put a zero byte in front of the modulus when its
467//! top bit is set.
468//! Exactly one such byte is accepted in `n`, and dropped.
469//! The other RSA values must be minimally encoded, and multi-prime keys (`oth`) are not
470//! supported.
471//!
472//! ### JWK thumbprints
473//!
474//! `jwk_thumbprint()` returns the JWK thumbprint of a key: the SHA-256 hash of its
475//! required public members, base64url-encoded.
476//! A key pair and its public key have the same thumbprint, which doesn't depend on
477//! `kid` or any other metadata.
478//!
479//! The thumbprint of an ML-DSA key also covers its algorithm.
480//!
481//! A thumbprint makes a good key identifier:
482//!
483//! ```rust,ignore
484//! let thumbprint = key_pair.jwk_thumbprint();
485//! let key_pair = key_pair.with_key_id(&thumbprint);
486//! ```
487//!
488//! `create_key_id()` keeps its original definition, a hash of the raw public key, so
489//! that existing identifiers don't change.
490//!
491//! The `sha1_thumbprint()` and `sha256_thumbprint()` methods of Ed25519 and RSA public
492//! keys hash their DER encoding.
493//! They are not JWK thumbprints.
494//!
495//! ## Advanced usage
496//!
497//! ### Custom claims
498//!
499//! Claim objects support all the standard claims by default, and they can be set directly or via convenient helpers:
500//!
501//! ```rust,ignore
502//! let claims = Claims::create(Duration::from_hours(2)).
503//! with_issuer("Example issuer").with_subject("Example subject");
504//! ```
505//!
506//! But application-defined claims can also be used. These simply have to be present in a serializable type (this requires the `serde` crate):
507//!
508//! ```rust,ignore
509//! #[derive(Serialize, Deserialize)]
510//! struct MyAdditionalData {
511//! user_is_admin: bool,
512//! user_country: String,
513//! }
514//! let my_additional_data = MyAdditionalData {
515//! user_is_admin: false,
516//! user_country: "FR".to_string(),
517//! };
518//! ```
519//!
520//! Claim creation with custom data:
521//!
522//! ```rust,ignore
523//! let claims = Claims::with_custom_claims(my_additional_data, Duration::from_secs(30));
524//! ```
525//!
526//! Claim verification with custom data. Note the presence of the custom data type:
527//!
528//! ```rust,ignore
529//! let claims = public_key.verify_token::<MyAdditionalData>(&token, None)?;
530//! let user_is_admin = claims.custom.user_is_admin;
531//! ```
532//!
533//! ### Peeking at metadata before verification
534//!
535//! Properties such as the key identifier can be useful prior to tag or signature verification in order to pick the right key out of a set.
536//!
537//! ```rust,ignore
538//! let metadata = Token::decode_metadata(&token)?;
539//! let key_id = metadata.key_id();
540//! let algorithm = metadata.algorithm();
541//! // all other standard properties are also accessible
542//! ```
543//!
544//! **IMPORTANT:** neither the key ID nor the algorithm can be trusted. This is an unfixable design flaw of the JWT standard.
545//!
546//! As a result, `algorithm` should be used only for debugging purposes, and never to select a key type.
547//! Similarly, `key_id` should be used only to select a key in a set of keys made for the same algorithm.
548//!
549//! At the bare minimum, verification using `HS*` must be prohibited if a signature scheme was originally used to create the token.
550//!
551//! ### Creating and attaching key identifiers
552//!
553//! Key identifiers indicate to verifiers what public key (or shared key) should be used for verification.
554//! They can be attached at any time to existing shared keys, key pairs and public keys:
555//!
556//! ```rust,ignore
557//! let public_key_with_id = public_key.with_key_id(&"unique key identifier");
558//! ```
559//!
560//! Instead of delegating this to applications, `jwt-simple` can also create such an identifier for an existing key:
561//!
562//! ```rust,ignore
563//! let key_id = public_key.create_key_id();
564//! ```
565//!
566//! This creates a text-encoded identifier for the key, attaches it, and returns it.
567//!
568//! For a standard identifier that other implementations can compute too, use the key's
569//! [JWK thumbprint](#jwk-thumbprints).
570//!
571//! If an identifier has been attached to a shared key or a key pair, tokens created with them will include it.
572//!
573//! ### Mitigations against replay attacks
574//!
575//! `jwt-simple` includes mechanisms to mitigate replay attacks:
576//!
577//! - Nonces can be created and attached to new tokens using the `create_nonce()` claim function. The verification procedure can later reject any token that doesn't include the expected nonce (`required_nonce` verification option).
578//! - The verification procedure can reject tokens created too long ago, no matter what their expiration date is. This prevents tokens from malicious (or compromised) signers from being used for too long.
579//! - The verification procedure can reject tokens created before a date. For a given user, the date of the last successful authentication can be stored in a database, and used later along with this option to reject older (replayed) tokens.
580//!
581//! ### Salted keys
582//!
583//! Symmetric keys, such as the ones used with the `HS256`, `HS384`, `HS512` and `BLAKE2B` algorithms, are simple and fast, but have a major downside: signature and verification use the exact same key. Therefore, an adversary having access to the verifier key can forge arbitrary, valid tokens.
584//!
585//! Salted keys mitigate this issue in the following way:
586//!
587//! - A random signer salt is created and attached to the shared key. This salt is meant to be known only by the signer.
588//! - Another salt is computed from the signer salt and is meant to be used for verification.
589//! - The verifier salt is used to verify the signer salt, which is included in tokens in the `salt` JWT header.
590//!
591//! If the verifier has access to tokens, it can forge arbitrary tokens. But given only the verification code and keys, this is impossible. This greatly improves the security of symmetric keys used for verification on 3rd party servers, such as CDNs.
592//!
593//! A salt binds to a key, and can be of any length. The `generate_with_salt()` function generates both a random symmetric key, and a 32-byte salt.
594//!
595//! Example usage:
596//!
597//! ```rust,ignore
598//! // Create a random key and a signer salt
599//! let key = HS256Key::generate_with_salt();
600//! let claims = Claims::create(Duration::from_secs(86400));
601//! let token = key.authenticate(claims).unwrap();
602//! ```
603//!
604//! A salt is a `Salt` enum, because it can be either a salt for signing, or a salt for verification.
605//! It can be saved and restored:
606//!
607//! ```rust,ignore
608//! // Get the salt
609//! let salt = key.salt();
610//! // Attach an existing salt to a key
611//! key.attach_salt(salt)?;
612//! ```
613//!
614//! Given a signer salt, the corresponding verifier salt can be computed:
615//!
616//! ```rust,ignore
617//! // Compute the verifier salt, given a signer salt
618//! let verifier_salt = key.verifier_salt()?;
619//! ```
620//!
621//! The verifier salt doesn't have to be secret, and can even be hard-coded in the verification code.
622//!
623//! Verification:
624//!
625//! ```rust,ignore
626//! let verifier_salt = Salt::Verifier(verifier_salt_bytes);
627//! key.attach_salt(verifier_salt)?;
628//! let claims = key.verify_token::<NoCustomClaims>(&token, None)?;
629//! ```
630//!
631//! ### CWT (CBOR) support
632//!
633//! The development code includes a `cwt` cargo feature that enables experimental parsing and validation of CWT tokens.
634//!
635//! Please note that CWT doesn't support custom claims. The required identifiers [haven't been standardized yet](https://www.iana.org/assignments/cwt/cwt.xhtml).
636//!
637//! Also, the existing Rust crates for JSON and CBOR deserialization are not safe. An untrusted party can send a serialized object that requires a lot of memory and CPU to deserialize. Band-aids have been added for JSON, but with the current Rust tooling, it would be tricky to implement for CBOR.
638//!
639//! As a mitigation, we highly recommend rejecting tokens that would be too large in the context of your application. That can be done with the `max_token_length` verification option.
640//!
641//! ### Specifying header options
642//!
643//! It is possible to change the content type (`cty`) and signature type (`typ`) fields of a signed JWT by using the `sign_with_options`/`authenticate_with_options` functions and passing in a `HeaderOptions` struct:
644//!
645//! ``` rust,ignore
646//! let options = HeaderOptions {
647//! content_type: Some("foo".into()),
648//! signature_type: Some("foo+JWT".into()),
649//! ..Default::default()
650//! };
651//! key_pair.sign_with_options(claims, &options).unwrap();
652//! ```
653//!
654//! By default, generated JWTs will have a signature type field containing the string "JWT", and the content type field will not be present.
655//!
656//! ### Validating content and signature types
657//!
658//! By default, `jwt_simple` ignores the `content_type` field when doing validation, and checks `signature_type` to ensure it is either exactly `JWT` or ends in `+JWT`, case insensitive, if it is present. Both fields may instead be case-insensitively compared against an expected string:
659//!
660//! ```rust,ignore
661//! options.required_signature_type = Some("JWT".into());
662//! options.required_content_type = Some("foo+jwt".into());
663//! ```
664//!
665//! When validating CWTs, note that CWTs do not have a `content_type` field in their header, and therefore attempting to match a specific one by setting `required_content_type` during validation will **always result in an error**.
666//!
667//!
668//! ## Working around compilation issues with the `boring` crate
669//!
670//! As a temporary workaround for portability issues with one of the dependencies (the `boring` crate), this library can be compiled to use only Rust implementations.
671//!
672//! In order to do so, import the crate with `default-features = false, features = ["pure-rust"]` in your Cargo configuration.
673//!
674//! Do not do it unconditionally. This is only required for very specific setups and targets, and only until issues with the `boring` crate have been solved. The way to configure this in Cargo may also change in future versions.
675//!
676//! Static builds targeting the `musl` library don't require that workaround. Just use [`cargo-zigbuild`](https://github.com/rust-cross/cargo-zigbuild) to build your project.
677//!
678//! ## Usage in Web browsers
679//!
680//! The `wasm32-freestanding` target (still sometimes called `wasm32-unknown-unknown` in Rust) is supported (as in "it compiles").
681//!
682//! However, using a native JavaScript implementation is highly recommended instead. There are high-quality JWT implementations in JavaScript, leveraging the WebCrypto API, that provide better performance and security guarantees than a WebAssembly module.
683//!
684//! ## Why yet another JWT crate
685//!
686//! This crate is not an endorsement of JWT. JWT is [an awful design](https://tools.ietf.org/html/rfc8725), and one of the many examples that "but this is a standard" doesn't necessarily mean that it is good.
687//!
688//! I would highly recommend [PASETO](https://github.com/paragonie/paseto) or [Biscuit](https://github.com/eclipse-biscuit/biscuit) instead if you control both token creation and verification.
689//!
690//! However, JWT is still widely used in the industry, and remains absolutely mandatory to communicate with popular APIs.
691//!
692//! This crate was designed to:
693//!
694//! - Be simple to use, even for people who are new to Rust
695//! - Avoid common JWT API pitfalls
696//! - Support features widely in use. I'd love to limit the algorithm choices to Ed25519, but other methods are required to connect to existing APIs, so we provide them (with the exception of the `None` signature method for obvious reasons).
697//! - Minimize code complexity and external dependencies
698//! - Automatically perform common tasks to prevent misuse. Signature verification and claims validation happen automatically instead of relying on applications.
699//! - Still allow power users to access everything JWT tokens include if they really need to
700//! - Work out of the box in a WebAssembly environment, so that it can be used in function-as-a-service platforms.
701#![forbid(unsafe_code)]
702
703#[cfg(all(feature = "pure-rust", feature = "optimal"))]
704compile_error!("jwt-simple: the `optimal` feature is only available when the `pure-rust` feature is disabled - Consider disabling default Cargo features.");
705
706#[cfg(all(not(feature = "pure-rust"), not(feature = "optimal")))]
707compile_error!("jwt-simple: the `optimal` feature is required when the `pure-rust` feature is disabled - Consider enabling default Cargo features.");
708
709pub mod algorithms;
710pub mod claims;
711pub mod common;
712#[cfg(feature = "cwt")]
713pub mod cwt_token;
714#[cfg(feature = "jwe")]
715pub mod jwe_header;
716#[cfg(feature = "jwe")]
717pub mod jwe_token;
718pub mod token;
719
720mod jwt_header;
721mod serde_additions;
722
723pub mod reexports {
724 pub use anyhow;
725 pub use coarsetime;
726 pub use ct_codecs;
727 pub use rand;
728 pub use serde;
729 pub use serde_json;
730 pub use thiserror;
731 pub use zeroize;
732}
733
734mod error;
735pub use error::{Error, JWTError};
736
737pub mod prelude {
738 pub use std::collections::HashSet;
739
740 pub use coarsetime::{self, Clock, Duration, UnixTimeStamp};
741 pub use ct_codecs::{
742 Base64, Base64NoPadding, Base64UrlSafe, Base64UrlSafeNoPadding, Decoder as _, Encoder as _,
743 };
744 pub use serde::{Deserialize, Serialize};
745
746 pub use crate::algorithms::*;
747 pub use crate::claims::*;
748 pub use crate::common::*;
749 #[cfg(feature = "cwt")]
750 pub use crate::cwt_token::*;
751 #[cfg(feature = "jwe")]
752 pub use crate::jwe_token::{DecryptionOptions, EncryptionOptions, JWEToken, JWETokenMetadata};
753 pub use crate::token::*;
754
755 mod hashset_from_strings {
756 use std::collections::HashSet;
757
758 pub trait HashSetFromStringsT {
759 /// Create a set from a list of strings
760 fn from_strings(strings: &[impl ToString]) -> HashSet<String> {
761 strings.iter().map(|x| x.to_string()).collect()
762 }
763 }
764
765 impl HashSetFromStringsT for HashSet<String> {}
766 }
767
768 pub use hashset_from_strings::HashSetFromStringsT as _;
769}
770
771#[cfg(test)]
772mod tests {
773 use crate::prelude::*;
774
775 const RSA_KP_PEM: &str = r"
776-----BEGIN RSA PRIVATE KEY-----
777MIIEpAIBAAKCAQEAyqq0N5u8Jvl+BLH2VMP/NAv/zY9T8mSq0V2Gk5Ql5H1a+4qi
7783viorUXG3AvIEEccpLsW85ps5+I9itp74jllRjA5HG5smbb+Oym0m2Hovfj6qP/1
779m1drQg8oth6tNmupNqVzlGGWZLsSCBLuMa3pFaPhoxl9lGU3XJIQ1/evMkOb98I3
780hHb4ELn3WGtNlAVkbP20R8sSii/zFjPqrG/NbSPLyAl1ctbG2d8RllQF1uRIqYQj
78185yx73hqQCMpYWU3d9QzpkLf/C35/79qNnSKa3t0cyDKinOY7JGIwh8DWAa4pfEz
782gg56yLcilYSSohXeaQV0nR8+rm9J8GUYXjPK7wIDAQABAoIBAQCpeRPYyHcPFGTH
7834lU9zuQSjtIq/+bP9FRPXWkS8bi6GAVEAUtvLvpGYuoGyidTTVPrgLORo5ncUnjq
784KwebRimlBuBLIR/Zboery5VGthoc+h4JwniMnQ6JIAoIOSDZODA5DSPYeb58n15V
785uBbNHkOiH/eoHsG/nOAtnctN/cXYPenkCfeLXa3se9EzkcmpNGhqCBL/awtLU17P
786Iw7XxsJsRMBOst4Aqiri1GQI8wqjtXWLyfjMpPR8Sqb4UpTDmU1wHhE/w/+2lahC
787Tu0/+sCWj7TlafYkT28+4pAMyMqUT6MjqdmGw8lD7/vXv8TF15NU1cUv3QSKpVGe
78850vlB1QpAoGBAO1BU1evrNvA91q1bliFjxrH3MzkTQAJRMn9PBX29XwxVG7/HlhX
7890tZRSR92ZimT2bAu7tH0Tcl3Bc3NwEQrmqKlIMqiW+1AVYtNjuipIuB7INb/TUM3
790smEh+fn3yhMoVxbbh/klR1FapPUFXlpNv3DJHYM+STqLMhl9tEc/I7bLAoGBANqt
791zR6Kovf2rh7VK/Qyb2w0rLJE7Zh/WI+r9ubCba46sorqkJclE5cocxWuTy8HWyQp
792spxzLP1FQlsI+MESgRLueoH3HtB9lu/pv6/8JlNjU6SzovfUZ0KztVUyUeB4vAcH
793pGcf2CkUtoYc8YL22Ybck3s8ThIdnY5zphCF55PtAoGAf46Go3c05XVKx78R05AD
794D2/y+0mnSGSzUjHPMzPyadIPxhltlCurlERhnwPGC4aNHFcvWTwS8kUGns6HF1+m
795JNnI1okSCW10UI/jTJ1avfwU/OKIBKKWSfi9cDJTt5cRs51V7pKnVEr6sy0uvDhe
796u+G091HuhwY9ak0WNtPwfJ8CgYEAuRdoyZQQso7x/Bj0tiHGW7EOB2n+LRiErj6g
797odspmNIH8zrtHXF9bnEHT++VCDpSs34ztuZpywnHS2SBoHH4HD0MJlszksbqbbDM
7981bk3+1bUIlEF/Hyk1jljn3QTB0tJ4y1dwweaH9NvVn7DENW9cr/aePGnJwA4Lq3G
799fq/IPlUCgYAuqgJQ4ztOq0EaB75xgqtErBM57A/+lMWS9eD/euzCEO5UzWVaiIJ+
800nNDmx/jvSrxA1Ih8TEHjzv4ezLFYpaJrTst4Mjhtx+csXRJU9a2W6HMXJ4Kdn8rk
801PBziuVURslNyLdlFsFlm/kfvX+4Cxrbb+pAGETtRTgmAoCDbvuDGRQ==
802-----END RSA PRIVATE KEY-----
803 ";
804
805 const RSA_PK_PEM: &str = r"
806-----BEGIN PUBLIC KEY-----
807MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyqq0N5u8Jvl+BLH2VMP/
808NAv/zY9T8mSq0V2Gk5Ql5H1a+4qi3viorUXG3AvIEEccpLsW85ps5+I9itp74jll
809RjA5HG5smbb+Oym0m2Hovfj6qP/1m1drQg8oth6tNmupNqVzlGGWZLsSCBLuMa3p
810FaPhoxl9lGU3XJIQ1/evMkOb98I3hHb4ELn3WGtNlAVkbP20R8sSii/zFjPqrG/N
811bSPLyAl1ctbG2d8RllQF1uRIqYQj85yx73hqQCMpYWU3d9QzpkLf/C35/79qNnSK
812a3t0cyDKinOY7JGIwh8DWAa4pfEzgg56yLcilYSSohXeaQV0nR8+rm9J8GUYXjPK
8137wIDAQAB
814-----END PUBLIC KEY-----
815 ";
816
817 #[test]
818 fn hs384() {
819 let key = HS384Key::from_bytes(b"your-256-bit-secret").with_key_id("my-key-id");
820 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
821 let token = key.authenticate(claims).unwrap();
822 let options = VerificationOptions {
823 allowed_issuers: Some(HashSet::from_strings(&["test issuer"])),
824 ..Default::default()
825 };
826 let _claims = key
827 .verify_token::<NoCustomClaims>(&token, Some(options))
828 .unwrap();
829 }
830
831 #[test]
832 fn blake2b() {
833 let key = Blake2bKey::from_bytes(b"your-256-bit-secret").with_key_id("my-key-id");
834 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
835 let token = key.authenticate(claims).unwrap();
836 let options = VerificationOptions {
837 allowed_issuers: Some(HashSet::from_strings(&["test issuer"])),
838 ..Default::default()
839 };
840 let _claims = key
841 .verify_token::<NoCustomClaims>(&token, Some(options))
842 .unwrap();
843 }
844
845 #[test]
846 fn rs256() {
847 let key_pair = RS256KeyPair::from_pem(RSA_KP_PEM).unwrap();
848 let claims = Claims::create(Duration::from_secs(86400));
849 let token = key_pair.sign(claims).unwrap();
850 let pk = RS256PublicKey::from_pem(RSA_PK_PEM).unwrap();
851 let _claims = pk.verify_token::<NoCustomClaims>(&token, None).unwrap();
852 let components = pk.to_components();
853 let hex_e = Base64::encode_to_string(components.e).unwrap();
854 let _e = Base64::decode_to_vec(hex_e, None).unwrap();
855 }
856
857 #[test]
858 fn ps384() {
859 let key_pair = PS384KeyPair::generate(2048).unwrap();
860 let claims = Claims::create(Duration::from_secs(86400));
861 let token = key_pair.sign(claims).unwrap();
862 let _claims = key_pair
863 .public_key()
864 .verify_token::<NoCustomClaims>(&token, None)
865 .unwrap();
866 }
867
868 #[test]
869 fn es256() {
870 let key_pair = ES256KeyPair::generate();
871 let claims = Claims::create(Duration::from_secs(86400));
872 let token = key_pair.sign(claims).unwrap();
873 let _claims = key_pair
874 .public_key()
875 .verify_token::<NoCustomClaims>(&token, None)
876 .unwrap();
877 }
878
879 #[test]
880 fn es384() {
881 let key_pair = ES384KeyPair::generate();
882 let claims = Claims::create(Duration::from_secs(86400));
883 let token = key_pair.sign(claims).unwrap();
884 let _claims = key_pair
885 .public_key()
886 .verify_token::<NoCustomClaims>(&token, None)
887 .unwrap();
888 }
889
890 #[test]
891 fn es256k() {
892 let key_pair = ES256kKeyPair::generate();
893 let claims = Claims::create(Duration::from_secs(86400));
894 let token = key_pair.sign(claims).unwrap();
895 let _claims = key_pair
896 .public_key()
897 .verify_token::<NoCustomClaims>(&token, None)
898 .unwrap();
899 }
900
901 #[test]
902 fn ed25519() {
903 #[derive(Serialize, Deserialize)]
904 struct CustomClaims {
905 is_custom: bool,
906 }
907
908 let key_pair = Ed25519KeyPair::generate();
909 let mut pk = key_pair.public_key();
910 let key_id = pk.create_key_id();
911 let key_pair = key_pair.with_key_id(key_id);
912 let public_key = key_pair.public_key(); // Get public key after setting key_id
913 let custom_claims = CustomClaims { is_custom: true };
914 let claims = Claims::with_custom_claims(custom_claims, Duration::from_secs(86400));
915 let token = key_pair.sign(claims).unwrap();
916 let options = VerificationOptions {
917 required_key_id: Some(key_id.to_string()),
918 ..Default::default()
919 };
920 let claims: JWTClaims<CustomClaims> = public_key
921 .verify_token::<CustomClaims>(&token, Some(options))
922 .unwrap();
923 assert!(claims.custom.is_custom);
924 }
925
926 #[test]
927 fn ed25519_der() {
928 let key_pair = Ed25519KeyPair::generate();
929 let der = key_pair.to_der();
930 let key_pair2 = Ed25519KeyPair::from_der(&der).unwrap();
931 assert_eq!(key_pair.to_bytes(), key_pair2.to_bytes());
932 }
933
934 #[test]
935 fn mldsa65() {
936 #[derive(Serialize, Deserialize)]
937 struct CustomClaims {
938 is_custom: bool,
939 }
940
941 let key_pair = MLDSA65KeyPair::generate();
942 let mut pk = key_pair.public_key();
943 let key_id = pk.create_key_id();
944 let key_pair = key_pair.with_key_id(key_id);
945 let public_key = key_pair.public_key();
946 let custom_claims = CustomClaims { is_custom: true };
947 let claims = Claims::with_custom_claims(custom_claims, Duration::from_secs(86400));
948 let token = key_pair.sign(claims).unwrap();
949 let options = VerificationOptions {
950 required_key_id: Some(key_id.to_string()),
951 ..Default::default()
952 };
953 let claims: JWTClaims<CustomClaims> = public_key
954 .verify_token::<CustomClaims>(&token, Some(options))
955 .unwrap();
956 assert!(claims.custom.is_custom);
957 }
958
959 #[test]
960 fn mldsa_key_round_trip() {
961 let key_pair = MLDSA44KeyPair::generate();
962 let key_pair2 = MLDSA44KeyPair::from_bytes(&key_pair.to_bytes()).unwrap();
963 assert_eq!(key_pair.to_bytes(), key_pair2.to_bytes());
964
965 let public_key = key_pair.public_key();
966 let public_key2 = MLDSA44PublicKey::from_bytes(&public_key.to_bytes()).unwrap();
967 assert_eq!(public_key.to_bytes(), public_key2.to_bytes());
968
969 let claims = Claims::create(Duration::from_secs(86400));
970 let token = key_pair2.sign(claims).unwrap();
971 let _claims = public_key2
972 .verify_token::<NoCustomClaims>(&token, None)
973 .unwrap();
974 }
975
976 #[test]
977 fn mldsa_wrong_key_rejected() {
978 let key_pair = MLDSA87KeyPair::generate();
979 let claims = Claims::create(Duration::from_secs(86400));
980 let token = key_pair.sign(claims).unwrap();
981
982 let other_public_key = MLDSA87KeyPair::generate().public_key();
983 let res = other_public_key.verify_token::<NoCustomClaims>(&token, None);
984 assert!(res.is_err());
985
986 let eddsa_public_key = Ed25519KeyPair::generate().public_key();
987 let res = eddsa_public_key.verify_token::<NoCustomClaims>(&token, None);
988 assert!(res.is_err());
989 }
990
991 #[test]
992 fn require_nonce() {
993 let key = HS256Key::generate();
994 let mut claims = Claims::create(Duration::from_hours(1));
995 let nonce = claims.create_nonce();
996 let token = key.authenticate(claims).unwrap();
997
998 let options = VerificationOptions {
999 required_nonce: Some(nonce),
1000 ..Default::default()
1001 };
1002 key.verify_token::<NoCustomClaims>(&token, Some(options))
1003 .unwrap();
1004 }
1005
1006 #[test]
1007 fn eddsa_pem() {
1008 let sk_pem = "-----BEGIN PRIVATE KEY-----
1009MC4CAQAwBQYDK2VwBCIEIMXY1NUbUe/3dW2YUoKW5evsnCJPMfj60/q0RzGne3gg
1010-----END PRIVATE KEY-----
1011";
1012 let pk_pem = "-----BEGIN PUBLIC KEY-----
1013MCowBQYDK2VwAyEAyrRjJfTnhMcW5igzYvPirFW5eUgMdKeClGzQhd4qw+Y=
1014-----END PUBLIC KEY-----
1015";
1016 let kp = Ed25519KeyPair::from_pem(sk_pem).unwrap();
1017 assert_eq!(kp.public_key().to_pem(), pk_pem);
1018 }
1019
1020 #[test]
1021 fn key_metadata() {
1022 let mut key_pair = Ed25519KeyPair::generate();
1023 let thumbprint = key_pair.public_key().sha1_thumbprint();
1024 let key_metadata = KeyMetadata::default()
1025 .with_certificate_sha1_thumbprint(&thumbprint)
1026 .unwrap();
1027 key_pair.attach_metadata(key_metadata).unwrap();
1028
1029 let claims = Claims::create(Duration::from_secs(86400));
1030 let token = key_pair.sign(claims).unwrap();
1031
1032 let decoded_metadata = Token::decode_metadata(&token).unwrap();
1033 assert_eq!(
1034 decoded_metadata.certificate_sha1_thumbprint(),
1035 Some(thumbprint.as_ref())
1036 );
1037 let _ = key_pair
1038 .public_key()
1039 .verify_token::<NoCustomClaims>(&token, None)
1040 .unwrap();
1041 }
1042
1043 #[test]
1044 fn set_header_content_type() {
1045 let key_pair = Ed25519KeyPair::generate();
1046 let claims = Claims::create(Duration::from_secs(86400));
1047 let token = key_pair
1048 .sign_with_options(
1049 claims,
1050 &HeaderOptions {
1051 content_type: Some("foo".into()),
1052 ..Default::default()
1053 },
1054 )
1055 .unwrap();
1056 let decoded_metadata = Token::decode_metadata(&token).unwrap();
1057 assert_eq!(
1058 decoded_metadata.jwt_header.content_type.as_deref(),
1059 Some("foo")
1060 );
1061 let _ = key_pair
1062 .public_key()
1063 .verify_token::<NoCustomClaims>(&token, None)
1064 .unwrap();
1065 }
1066
1067 #[test]
1068 fn set_header_signature_type() {
1069 let key_pair = Ed25519KeyPair::generate();
1070 let claims = Claims::create(Duration::from_secs(86400));
1071 let token = key_pair
1072 .sign_with_options(
1073 claims,
1074 &HeaderOptions {
1075 signature_type: Some("etc+jwt".into()),
1076 ..Default::default()
1077 },
1078 )
1079 .unwrap();
1080 let decoded_metadata = Token::decode_metadata(&token).unwrap();
1081 assert_eq!(
1082 decoded_metadata.jwt_header.signature_type.as_deref(),
1083 Some("etc+jwt")
1084 );
1085 let _ = key_pair
1086 .public_key()
1087 .verify_token::<NoCustomClaims>(&token, None)
1088 .unwrap();
1089 }
1090
1091 #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
1092 #[test]
1093 fn expired_token() {
1094 let key = HS256Key::generate();
1095 let claims = Claims::create(Duration::from_secs(1));
1096 let token = key.authenticate(claims).unwrap();
1097 std::thread::sleep(std::time::Duration::from_secs(2));
1098 let options = VerificationOptions {
1099 time_tolerance: None,
1100 ..Default::default()
1101 };
1102 let claims = key.verify_token::<NoCustomClaims>(&token, None);
1103 assert!(claims.is_ok());
1104 let claims = key.verify_token::<NoCustomClaims>(&token, Some(options));
1105 assert!(claims.is_err());
1106 }
1107
1108 #[test]
1109 fn salt() {
1110 let mut key = HS256Key::generate_with_salt();
1111 let claims = Claims::create(Duration::from_secs(86400));
1112 let token = key.authenticate(claims).unwrap();
1113
1114 let res = key.verify_token::<NoCustomClaims>(&token, None);
1115 assert!(res.is_err());
1116
1117 let verifier_salt = key.verifier_salt().unwrap();
1118 key.attach_salt(verifier_salt).unwrap();
1119 key.verify_token::<NoCustomClaims>(&token, None).unwrap();
1120 }
1121
1122 #[test]
1123 fn salt2() {
1124 let mut key = HS256Key::generate();
1125 let claims = Claims::create(Duration::from_secs(86400));
1126 let token = key.authenticate(claims).unwrap();
1127
1128 key.verify_token::<NoCustomClaims>(&token, None).unwrap();
1129
1130 let verifier_salt = Salt::Verifier(b"salt".to_vec());
1131 key.attach_salt(verifier_salt).unwrap();
1132 let res = key.verify_token::<NoCustomClaims>(&token, None);
1133 assert!(res.is_err());
1134 }
1135
1136 #[test]
1137 fn weak_key_rejected_on_authenticate() {
1138 let key = HS256Key::from_bytes(b"short");
1139 let claims = Claims::create(Duration::from_secs(86400));
1140 let res = key.authenticate(claims);
1141 assert!(res.is_err());
1142 assert!(res.unwrap_err().to_string().contains("Weak key"));
1143 }
1144
1145 #[test]
1146 fn weak_key_rejected_on_verify() {
1147 let valid_key = HS256Key::generate();
1148 let claims = Claims::create(Duration::from_secs(86400));
1149 let token = valid_key.authenticate(claims).unwrap();
1150
1151 let weak_key = HS256Key::from_bytes(b"11-bytes..");
1152 let res = weak_key.verify_token::<NoCustomClaims>(&token, None);
1153 assert!(res.is_err());
1154 assert!(res.unwrap_err().to_string().contains("Weak key"));
1155 }
1156
1157 #[test]
1158 fn min_key_length_accepted() {
1159 let key = HS256Key::from_bytes(b"12-bytes...!");
1160 let claims = Claims::create(Duration::from_secs(86400));
1161 let token = key.authenticate(claims).unwrap();
1162 key.verify_token::<NoCustomClaims>(&token, None).unwrap();
1163 }
1164
1165 #[test]
1166 fn weak_key_rejected_hs384() {
1167 let key = HS384Key::from_bytes(b"short");
1168 let claims = Claims::create(Duration::from_secs(86400));
1169 assert!(key.authenticate(claims).is_err());
1170 }
1171
1172 #[test]
1173 fn weak_key_rejected_hs512() {
1174 let key = HS512Key::from_bytes(b"short");
1175 let claims = Claims::create(Duration::from_secs(86400));
1176 assert!(key.authenticate(claims).is_err());
1177 }
1178
1179 #[test]
1180 fn weak_key_rejected_blake2b() {
1181 let key = Blake2bKey::from_bytes(b"short");
1182 let claims = Claims::create(Duration::from_secs(86400));
1183 assert!(key.authenticate(claims).is_err());
1184 }
1185
1186 #[cfg(feature = "jwe")]
1187 #[test]
1188 fn jwe_rsa_oaep() {
1189 let decryption_key = RsaOaepDecryptionKey::generate(2048).unwrap();
1190 let encryption_key = decryption_key.encryption_key();
1191
1192 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
1193 let token = encryption_key.encrypt(claims).unwrap();
1194
1195 let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None).unwrap();
1196 assert_eq!(claims.issuer, Some("test issuer".to_string()));
1197 }
1198
1199 #[cfg(feature = "jwe")]
1200 #[test]
1201 fn jwe_a256kw() {
1202 let key = A256KWKey::generate();
1203
1204 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
1205 let token = key.encrypt(claims).unwrap();
1206
1207 let claims: JWTClaims<NoCustomClaims> = key.decrypt_token(&token, None).unwrap();
1208 assert_eq!(claims.issuer, Some("test issuer".to_string()));
1209 }
1210
1211 #[cfg(feature = "jwe")]
1212 #[test]
1213 fn jwe_a256kw_from_bytes() {
1214 let raw_key = [0u8; 32];
1215 let key = A256KWKey::from_bytes(&raw_key).unwrap();
1216
1217 let claims = Claims::create(Duration::from_secs(86400));
1218 let token = key.encrypt(claims).unwrap();
1219
1220 let key2 = A256KWKey::from_bytes(&raw_key).unwrap();
1221 let _claims: JWTClaims<NoCustomClaims> = key2.decrypt_token(&token, None).unwrap();
1222 }
1223
1224 #[cfg(feature = "jwe")]
1225 #[test]
1226 fn jwe_a128kw() {
1227 let key = A128KWKey::generate();
1228
1229 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
1230 let token = key.encrypt(claims).unwrap();
1231
1232 let claims: JWTClaims<NoCustomClaims> = key.decrypt_token(&token, None).unwrap();
1233 assert_eq!(claims.issuer, Some("test issuer".to_string()));
1234 }
1235
1236 #[cfg(feature = "jwe")]
1237 #[test]
1238 fn jwe_a128kw_from_bytes() {
1239 let raw_key = [0u8; 16];
1240 let key = A128KWKey::from_bytes(&raw_key).unwrap();
1241
1242 let claims = Claims::create(Duration::from_secs(86400));
1243 let token = key.encrypt(claims).unwrap();
1244
1245 let key2 = A128KWKey::from_bytes(&raw_key).unwrap();
1246 let _claims: JWTClaims<NoCustomClaims> = key2.decrypt_token(&token, None).unwrap();
1247 }
1248
1249 #[cfg(feature = "jwe")]
1250 #[test]
1251 fn jwe_ecdh_es_a256kw() {
1252 let decryption_key = EcdhEsA256KWDecryptionKey::generate();
1253 let encryption_key = decryption_key.encryption_key();
1254
1255 let claims = Claims::create(Duration::from_secs(86400))
1256 .with_issuer("test issuer")
1257 .with_audience("test audience");
1258 let token = encryption_key.encrypt(claims).unwrap();
1259
1260 let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None).unwrap();
1261 assert_eq!(claims.issuer, Some("test issuer".to_string()));
1262 }
1263
1264 #[cfg(feature = "jwe")]
1265 #[test]
1266 fn jwe_ecdh_es_a128kw() {
1267 let decryption_key = EcdhEsA128KWDecryptionKey::generate();
1268 let encryption_key = decryption_key.encryption_key();
1269
1270 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
1271 let token = encryption_key.encrypt(claims).unwrap();
1272
1273 let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None).unwrap();
1274 assert_eq!(claims.issuer, Some("test issuer".to_string()));
1275 }
1276
1277 #[cfg(feature = "jwe")]
1278 #[test]
1279 fn jwe_custom_claims() {
1280 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1281 struct CustomClaims {
1282 user_id: u64,
1283 role: String,
1284 }
1285
1286 let key = A256KWKey::generate();
1287
1288 let custom = CustomClaims {
1289 user_id: 12345,
1290 role: "admin".to_string(),
1291 };
1292 let claims = Claims::with_custom_claims(custom, Duration::from_secs(86400))
1293 .with_issuer("test issuer");
1294 let token = key.encrypt(claims).unwrap();
1295
1296 let claims: JWTClaims<CustomClaims> = key.decrypt_token(&token, None).unwrap();
1297 assert_eq!(claims.issuer, Some("test issuer".to_string()));
1298 assert_eq!(claims.custom.user_id, 12345);
1299 assert_eq!(claims.custom.role, "admin");
1300 }
1301
1302 #[cfg(feature = "jwe")]
1303 #[test]
1304 fn jwe_with_content_encryption_a128gcm() {
1305 let key = A256KWKey::generate();
1306
1307 let claims = Claims::create(Duration::from_secs(86400));
1308 let options = EncryptionOptions {
1309 content_encryption: ContentEncryption::A128GCM,
1310 ..Default::default()
1311 };
1312 let token = key.encrypt_with_options(claims, &options).unwrap();
1313
1314 let metadata = A256KWKey::decode_metadata(&token).unwrap();
1315 assert_eq!(metadata.encryption(), "A128GCM");
1316
1317 let _claims: JWTClaims<NoCustomClaims> = key.decrypt_token(&token, None).unwrap();
1318 }
1319
1320 #[cfg(feature = "jwe")]
1321 #[test]
1322 fn jwe_metadata_decode() {
1323 let key = A256KWKey::generate().with_key_id("my-key");
1324
1325 let claims = Claims::create(Duration::from_secs(86400));
1326 let options = EncryptionOptions {
1327 content_type: Some("JWT".to_string()),
1328 ..Default::default()
1329 };
1330 let token = key.encrypt_with_options(claims, &options).unwrap();
1331
1332 let metadata = A256KWKey::decode_metadata(&token).unwrap();
1333 assert_eq!(metadata.algorithm(), "A256KW");
1334 assert_eq!(metadata.encryption(), "A256GCM");
1335 assert_eq!(metadata.key_id(), Some("my-key"));
1336 assert_eq!(metadata.content_type(), Some("JWT"));
1337 }
1338
1339 #[cfg(feature = "jwe")]
1340 #[test]
1341 fn jwe_wrong_key_fails() {
1342 let key1 = A256KWKey::generate();
1343 let key2 = A256KWKey::generate();
1344
1345 let claims = Claims::create(Duration::from_secs(86400));
1346 let token = key1.encrypt(claims).unwrap();
1347
1348 let result: Result<JWTClaims<NoCustomClaims>, _> = key2.decrypt_token(&token, None);
1349 assert!(result.is_err());
1350 }
1351
1352 #[cfg(feature = "jwe")]
1353 #[test]
1354 fn jwe_invalid_key_size_a256kw() {
1355 let result = A256KWKey::from_bytes(&[0u8; 16]);
1356 assert!(result.is_err());
1357 }
1358
1359 #[cfg(feature = "jwe")]
1360 #[test]
1361 fn jwe_invalid_key_size_a128kw() {
1362 let result = A128KWKey::from_bytes(&[0u8; 32]);
1363 assert!(result.is_err());
1364 }
1365
1366 #[cfg(feature = "jwe")]
1367 #[test]
1368 fn jwe_rsa_key_too_small() {
1369 let result = RsaOaepDecryptionKey::generate(1024);
1370 assert!(result.is_err());
1371 }
1372
1373 #[cfg(feature = "jwe")]
1374 #[test]
1375 fn jwe_critical_header_rejected() {
1376 use ct_codecs::{Base64UrlSafeNoPadding, Encoder};
1377
1378 let key = A256KWKey::generate();
1379 let claims = Claims::create(Duration::from_secs(86400));
1380 let token = key.encrypt(claims).unwrap();
1381
1382 let parts: Vec<&str> = token.split('.').collect();
1383 let header_bytes =
1384 ct_codecs::Base64UrlSafeNoPadding::decode_to_vec(parts[0], None).unwrap();
1385 let mut header: serde_json::Value = serde_json::from_slice(&header_bytes).unwrap();
1386 header["crit"] = serde_json::json!(["unknown-extension"]);
1387 let modified_header = serde_json::to_string(&header).unwrap();
1388 let modified_header_b64 =
1389 Base64UrlSafeNoPadding::encode_to_string(&modified_header).unwrap();
1390
1391 let modified_token = format!(
1392 "{}.{}.{}.{}.{}",
1393 modified_header_b64, parts[1], parts[2], parts[3], parts[4]
1394 );
1395
1396 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(&modified_token, None);
1397 assert!(result.is_err());
1398 }
1399
1400 #[cfg(feature = "jwe")]
1401 #[test]
1402 fn jwe_malformed_inputs_no_panic() {
1403 let key = A256KWKey::generate();
1404
1405 let malformed_inputs = [
1406 "",
1407 ".",
1408 "..",
1409 "...",
1410 "....",
1411 ".....",
1412 "a]]]]]",
1413 "a.b.c.d.e",
1414 "?????.?????.?????.?????.?????",
1415 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0",
1416 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.",
1417 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0..",
1418 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0...",
1419 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0....",
1420 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.a.b.c.d",
1421 "not-base64!@#$.valid.valid.valid.valid",
1422 "eyJhbGciOiJBMjU2S1cifQ.a.b.c.d",
1423 "eyJ9.a.b.c.d",
1424 "e30.a.b.c.d",
1425 &"a".repeat(100000),
1426 &format!("{}.{}.{}.{}.{}", "a".repeat(10000), "b", "c", "d", "e"),
1427 "eyJhbGciOiJXUk9ORyIsImVuYyI6IkEyNTZHQ00ifQ.a.b.c.d",
1428 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJXUk9ORyJ9.a.b.c.d",
1429 " . . . . ",
1430 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.AAAA.AAAA.AAAA.AAAA",
1431 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.....AAAA",
1432 ];
1433
1434 for input in &malformed_inputs {
1435 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(input, None);
1436 assert!(result.is_err(), "Expected error for input: {}", input);
1437 }
1438
1439 // Also test decode_metadata with malformed inputs
1440 for input in &malformed_inputs {
1441 let _ = A256KWKey::decode_metadata(input);
1442 }
1443 }
1444
1445 #[cfg(feature = "jwe")]
1446 #[test]
1447 fn jwe_truncated_token_no_panic() {
1448 let key = A256KWKey::generate();
1449 let claims = Claims::create(Duration::from_secs(86400));
1450 let token = key.encrypt(claims).unwrap();
1451
1452 // Test progressively truncated tokens
1453 for i in 0..token.len() {
1454 let truncated = &token[..i];
1455 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(truncated, None);
1456 assert!(result.is_err());
1457 }
1458 }
1459
1460 #[cfg(feature = "jwe")]
1461 #[test]
1462 fn jwe_corrupted_parts_no_panic() {
1463 let key = A256KWKey::generate();
1464 let claims = Claims::create(Duration::from_secs(86400));
1465 let token = key.encrypt(claims).unwrap();
1466 let parts: Vec<&str> = token.split('.').collect();
1467
1468 // Corrupt each part individually
1469 for i in 0..5 {
1470 let mut modified_parts: Vec<String> = parts.iter().map(|s| s.to_string()).collect();
1471 modified_parts[i] = "AAAA".to_string();
1472 let modified = modified_parts.join(".");
1473 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(&modified, None);
1474 assert!(result.is_err());
1475 }
1476
1477 // Empty each part individually
1478 for i in 0..5 {
1479 let mut modified_parts: Vec<String> = parts.iter().map(|s| s.to_string()).collect();
1480 modified_parts[i] = "".to_string();
1481 let modified = modified_parts.join(".");
1482 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(&modified, None);
1483 assert!(result.is_err());
1484 }
1485 }
1486
1487 #[cfg(feature = "jwe")]
1488 #[test]
1489 fn jwe_rsa_malformed_inputs_no_panic() {
1490 let key = RsaOaepDecryptionKey::generate(2048).unwrap();
1491
1492 let malformed_inputs = [
1493 "",
1494 ".....",
1495 "a.b.c.d.e",
1496 "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.a.b.c.d",
1497 ];
1498
1499 for input in &malformed_inputs {
1500 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(input, None);
1501 assert!(result.is_err());
1502 }
1503 }
1504
1505 #[cfg(feature = "jwe")]
1506 #[test]
1507 fn jwe_ecdh_malformed_inputs_no_panic() {
1508 let key = EcdhEsA256KWDecryptionKey::generate();
1509
1510 let malformed_inputs = [
1511 "",
1512 ".....",
1513 "a.b.c.d.e",
1514 "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00ifQ.a.b.c.d",
1515 // Valid header but missing epk
1516 "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00ifQ.AAAA.AAAA.AAAA.AAAA",
1517 ];
1518
1519 for input in &malformed_inputs {
1520 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(input, None);
1521 assert!(result.is_err());
1522 }
1523 }
1524}