Skip to main content

jwt_simple/
lib.rs

1//! [![GitHub CI](https://github.com/jedisct1/rust-jwt-simple/workflows/Rust/badge.svg)](https://github.com/jedisct1/rust-jwt-simple/actions)
2//! [![Docs.rs](https://docs.rs/jwt-simple/badge.svg)](https://docs.rs/jwt-simple/)
3//! [![crates.io](https://img.shields.io/crates/v/jwt-simple.svg)](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}