jwk_simple/lib.rs
1//! # jwk-simple
2//!
3//! A Rust library for working with JSON Web Keys (JWK) and JWK Sets (JWKS) as
4//! defined in RFC 7517, with full support for WASM environments and optional
5//! jwt-simple integration.
6//!
7//! ## Features
8//!
9//! - **RFC coverage (JOSE/JWK)**: Supports RFC 7517 (JWK), RFC 7518 (algorithms),
10//! RFC 8037 (OKP), RFC 9864 (Ed25519/Ed448 JOSE algorithms), and RFC 7638
11//! (thumbprints)
12//! - **Multiple key types**: RSA, EC (P-256, P-384, P-521, secp256k1),
13//! Symmetric (HMAC), and OKP (Ed25519, Ed448, X25519, X448)
14//! - **WASM compatible**: Core functionality works in WebAssembly environments
15//! - **Security-first**: Zeroize support for sensitive data, constant-time base64 encoding
16//! - **jwt-simple integration**: Optional feature for converting JWKs to jwt-simple key types
17//! - **Remote fetching**: Load JWKS from HTTP endpoints with caching support
18//! - **Strict selection API**: `KeySet::selector(...).select(...)` with typed errors
19//!
20//! ## Quick Start
21//!
22//! Parse a JWKS and strictly select a verification key:
23//!
24//! ```
25//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
26//! use jwk_simple::{Algorithm, KeyMatcher, KeyOperation, KeySet};
27//!
28//! let json = r#"{
29//! "keys": [{
30//! "kty": "RSA",
31//! "kid": "my-key-id",
32//! "use": "sig",
33//! "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
34//! "e": "AQAB"
35//! }]
36//! }"#;
37//!
38//! let jwks = serde_json::from_str::<KeySet>(json)?;
39//! let key = jwks
40//! .selector(&[Algorithm::Rs256])
41//! .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("my-key-id"))?;
42//! assert!(key.is_public_key_only());
43//! # Ok(())
44//! # }
45//! ```
46//!
47//! ## Feature Flags
48//!
49//! Feature definitions live in `Cargo.toml` (`[features]`), while this section
50//! documents expected usage and platform constraints.
51//!
52//! | Feature | Platform | Description |
53//! |---------|----------|-------------|
54//! | `jwt-simple` | all targets | Integration with the jwt-simple crate (requires a `jwt-simple` backend feature such as `jwt-simple/pure-rust`) |
55//! | `http` | all targets | Async HTTP fetching with `HttpKeyStore` |
56//! | `web-crypto` | `wasm32` only | WebCrypto integration for browser/WASM environments |
57//! | `cloudflare` | `wasm32` only | Cloudflare Workers support (Fetch API + KV cache) |
58//! | `moka` | non-`wasm32` only | In-memory `KeyCache` implementation using Moka |
59//!
60//! Invalid platform/feature combinations fail at compile time with clear
61//! `compile_error!` messages.
62//!
63//! ## Converting to jwt-simple keys
64//!
65//! With the `jwt-simple` feature enabled, you can convert JWKs to jwt-simple key types.
66//! Make sure your crate also enables a concrete `jwt-simple` backend feature
67//! (for example `jwt-simple/pure-rust`).
68//!
69#![cfg_attr(feature = "jwt-simple", doc = "```no_run")]
70#![cfg_attr(not(feature = "jwt-simple"), doc = "```ignore")]
71//! use jwk_simple::{Algorithm, KeyMatcher, KeyOperation, KeySet};
72//! use jwt_simple::prelude::*;
73//!
74//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
75//! # let json = "{}";
76//! # let token = "";
77//! let keyset: KeySet = serde_json::from_str(json)?;
78//! let jwk = keyset
79//! .selector(&[Algorithm::Rs256])
80//! .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("my-key-id"))?
81//! .clone();
82//!
83//! // Convert to jwt-simple key
84//! // Conversion failures use `JwtSimpleKeyConversionError`.
85//! let key: RS256PublicKey = jwk.try_into()?;
86//!
87//! // Use for JWT verification
88//! let claims = key.verify_token::<NoCustomClaims>(&token, None)?;
89//! # Ok(())
90//! # }
91//! ```
92//!
93//! ## Using with WebCrypto (Browser/WASM)
94//!
95//! With the `web-crypto` feature enabled, you can use JWKs with the browser's
96//! native SubtleCrypto API:
97//!
98#![cfg_attr(
99 all(feature = "web-crypto", any(target_arch = "wasm32", docsrs)),
100 doc = "```no_run"
101)]
102#![cfg_attr(
103 not(all(feature = "web-crypto", any(target_arch = "wasm32", docsrs))),
104 doc = "```ignore"
105)]
106//! use jwk_simple::{Algorithm, KeyMatcher, KeyOperation, KeySet};
107//! use std::convert::TryInto;
108//!
109//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
110//! # let json = r#"{"keys":[{"kty":"RSA","kid":"my-key-id","n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw","e":"AQAB"}]}"#;
111//! // Parse a JWKS
112//! let keyset: KeySet = serde_json::from_str(json)?;
113//! let key = keyset
114//! .selector(&[Algorithm::Rs256])
115//! .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("my-key-id"))?;
116//!
117//! // Check if the key is WebCrypto compatible
118//! if key.is_web_crypto_compatible() {
119//! // Import as a CryptoKey for verification.
120//! // Use the _for_alg variant because many JWKS keys (especially from OIDC
121//! // providers) omit the `alg` field, and WebCrypto requires the algorithm
122//! // to be known at import time for RSA and HMAC keys.
123//! let alg = Algorithm::Rs256; // typically from the JWT header
124//! let crypto_key = key.import_as_verify_key_for_alg(&alg).await?;
125//!
126//! // Or get the JsonWebKey directly
127//! let jwk: web_sys::JsonWebKey = key.try_into()?;
128//! }
129//! # Ok(())
130//! # }
131//! ```
132//!
133//! If the key's `alg` field is present, you can use the simpler
134//! `Key::import_as_verify_key` instead. EC keys always work without an explicit
135//! algorithm since the curve determines the WebCrypto parameters.
136//!
137//! **Note:** WebCrypto does not support OKP keys (Ed25519, Ed448, X25519, X448)
138//! or the secp256k1 curve. Use `Key::is_web_crypto_compatible()` to check
139//! compatibility before attempting to use a key with WebCrypto.
140//!
141//! ## Security
142//!
143//! This crate prioritizes security:
144//!
145//! - Private key parameters are zeroed from memory on drop via `zeroize`
146//! - Base64 encoding uses constant-time operations via `base64ct`
147//! - Debug output redacts sensitive key material
148//! - All fallible operations return `Result` types. The crate avoids panics,
149//! though standard trait implementations like `Index` follow normal Rust
150//! semantics and may panic on invalid input (e.g., out-of-bounds indexing)
151//! - Validation entry points are layered:
152//!
153//! | API | Structural key params | `use`/`key_ops` consistency | Cert metadata (`x5u`/`x5c`/`x5t`) | Alg suitability/strength | Op/alg compatibility | Private material capability | Selection policy |
154//! |-----|------------------------|-----------------------------|-----------------------------------|--------------------------|----------------------|-----------------------------|------------------|
155//! | [`Key::validate`] | yes | yes | yes | no | no | no | no |
156//! | [`Key::validate_for_use`] | yes | yes | yes | yes | yes | yes | no |
157//! | [`KeySet::selector(...).select(...)`](crate::jwks::KeySelector::select) | yes, per candidate | yes, per candidate | yes, per candidate | yes, per candidate | yes, up front | yes, per candidate | yes |
158//!
159//! PKIX trust validation for `x5c` chains is application-defined and out of
160//! scope for this crate.
161
162#![cfg_attr(docsrs, feature(doc_cfg))]
163#![deny(missing_docs)]
164#![forbid(unsafe_code)]
165#![warn(clippy::all)]
166
167// ---------------------------------------------------------------------------
168// Feature/target compatibility guards
169// ---------------------------------------------------------------------------
170//
171// docs.rs builds with all features enabled on a native target to render API docs.
172// We skip hard errors there so docs can still be generated for feature-gated APIs.
173#[cfg(all(feature = "web-crypto", not(target_arch = "wasm32"), not(docsrs)))]
174compile_error!("feature `web-crypto` is only supported on `wasm32` targets");
175
176#[cfg(all(feature = "cloudflare", not(target_arch = "wasm32"), not(docsrs)))]
177compile_error!("feature `cloudflare` is only supported on `wasm32` targets");
178
179#[cfg(all(feature = "moka", target_arch = "wasm32", not(docsrs)))]
180compile_error!("feature `moka` is not supported on `wasm32` targets");
181
182pub mod encoding;
183pub mod error;
184mod integrations;
185pub mod jwk;
186pub mod jwks;
187
188// Re-exports for convenience
189#[cfg(feature = "jwt-simple")]
190#[cfg_attr(docsrs, doc(cfg(feature = "jwt-simple")))]
191pub use error::JwtSimpleKeyConversionError;
192pub use error::{Error, IncompatibleKeyError, InvalidKeyError, ParseError, Result};
193pub use jwk::{
194 Algorithm, EcCurve, EcParams, Key, KeyOperation, KeyParams, KeyType, KeyUse, OkpCurve,
195 OkpParams, RsaOtherPrime, RsaParams, RsaParamsBuilder, SymmetricParams,
196};
197pub use jwks::{KeyFilter, KeyMatcher, KeySelector, KeySet, SelectionError};
198
199#[cfg(all(feature = "web-crypto", any(target_arch = "wasm32", docsrs)))]
200#[cfg_attr(docsrs, doc(cfg(all(feature = "web-crypto", target_arch = "wasm32"))))]
201pub use integrations::web_crypto;