axum_jwt_auth/
lib.rs

1//! A Rust library for JWT authentication with support for both local keys and remote JWKS (JSON Web Key Sets).
2//!
3//! This crate provides a flexible JWT authentication system that can:
4//! - Validate tokens using local RSA/HMAC keys
5//! - Automatically fetch and cache remote JWKS endpoints
6//! - Integrate seamlessly with the Axum web framework
7//! - Handle token validation with configurable options
8//! - Extract tokens from multiple sources (headers or cookies)
9//!
10//! It builds on top of the `jsonwebtoken` crate to provide higher-level authentication primitives
11//! while maintaining full compatibility with standard JWT implementations.
12//!
13//! # Quick Start
14//!
15//! ## Using Bearer Tokens (Default)
16//!
17//! ```ignore
18//! use std::sync::Arc;
19//! use axum::{Router, routing::get, Json, extract::FromRef};
20//! use axum_jwt_auth::{Claims, Decoder, LocalDecoder};
21//! use serde::{Deserialize, Serialize};
22//!
23//! #[derive(Deserialize, Serialize)]
24//! struct MyClaims {
25//!     sub: String,
26//!     exp: usize,
27//! }
28//!
29//! #[derive(Clone, FromRef)]
30//! struct AppState {
31//!     decoder: Decoder<MyClaims>,
32//! }
33//!
34//! async fn protected_handler(user: Claims<MyClaims>) -> Json<MyClaims> {
35//!     Json(user.claims)
36//! }
37//!
38//! let decoder = LocalDecoder::builder()
39//!     .keys(keys)
40//!     .validation(validation)
41//!     .build()
42//!     .unwrap();
43//!
44//! let state = AppState {
45//!     decoder: Arc::new(decoder),
46//! };
47//!
48//! let app = Router::new()
49//!     .route("/protected", get(protected_handler))
50//!     .with_state(state);
51//! ```
52//!
53//! ## Custom Token Extractors
54//!
55//! Use macros to easily define custom extractors:
56//!
57//! ```ignore
58//! use axum_jwt_auth::{define_header_extractor, define_cookie_extractor};
59//! use axum_jwt_auth::{Claims, HeaderTokenExtractor, CookieTokenExtractor};
60//!
61//! // Define custom extractors
62//! define_header_extractor!(XAuthToken, "x-auth-token");
63//! define_cookie_extractor!(AuthCookie, "auth_token");
64//!
65//! // Use in handlers
66//! async fn header_handler(user: Claims<MyClaims, HeaderTokenExtractor<XAuthToken>>) {
67//!     // Token extracted from "x-auth-token" header
68//! }
69//!
70//! async fn cookie_handler(user: Claims<MyClaims, CookieTokenExtractor<AuthCookie>>) {
71//!     // Token extracted from "auth_token" cookie
72//! }
73//! ```
74//!
75//! # Examples
76//!
77//! For full examples, see the [examples directory](https://github.com/cmackenzie1/axum-jwt-auth/blob/main/examples).
78
79mod axum;
80mod local;
81mod remote;
82
83use std::future::Future;
84use std::pin::Pin;
85use std::sync::Arc;
86
87use jsonwebtoken::TokenData;
88use serde::de::DeserializeOwned;
89use thiserror::Error;
90
91pub use crate::axum::{
92    AuthError, BearerTokenExtractor, Claims, CookieTokenExtractor, ExtractorConfig,
93    HeaderTokenExtractor, TokenExtractor,
94};
95pub use crate::local::LocalDecoder;
96pub use crate::remote::{
97    RemoteJwksDecoder, RemoteJwksDecoderBuilder, RemoteJwksDecoderConfig,
98    RemoteJwksDecoderConfigBuilder,
99};
100
101/// Errors that can occur during JWT decoding and validation.
102#[derive(Debug, thiserror::Error)]
103pub enum Error {
104    /// The JWT's `kid` (key ID) was not found in the key cache.
105    #[error("JWT key not found (kid: {0:?})")]
106    KeyNotFound(Option<String>),
107
108    /// Invalid decoder configuration (e.g., missing keys, algorithms, or audience).
109    #[error("Configuration error: {0}")]
110    Configuration(String),
111
112    /// JWT validation or decoding error from the `jsonwebtoken` crate.
113    #[error("JWT error: {0}")]
114    Jwt(#[from] jsonwebtoken::errors::Error),
115
116    /// HTTP request error when fetching remote JWKS.
117    #[error("HTTP request error: {0}")]
118    Reqwest(#[from] reqwest::Error),
119
120    /// JWKS refresh failed after exhausting all retry attempts.
121    #[error("JWKS refresh failed after {retry_count} attempts: {message}")]
122    JwksRefresh {
123        message: String,
124        retry_count: usize,
125        #[source]
126        source: Option<Box<dyn std::error::Error + Send + Sync>>,
127    },
128}
129
130/// Trait for decoding and validating JWT tokens.
131///
132/// Implemented by [`LocalDecoder`] and [`RemoteJwksDecoder`] to provide
133/// a unified interface for JWT validation with different key sources.
134pub trait JwtDecoder<T>: Send + Sync
135where
136    T: for<'de> DeserializeOwned,
137{
138    /// Decodes and validates a JWT token string, returning the parsed claims.
139    ///
140    /// # Errors
141    ///
142    /// Returns an error if the token is invalid, expired, has an invalid signature,
143    /// or if the key cannot be found (for remote decoders).
144    fn decode<'a>(
145        &'a self,
146        token: &'a str,
147    ) -> Pin<Box<dyn Future<Output = Result<TokenData<T>, Error>> + Send + 'a>>;
148}
149
150/// Type alias for a thread-safe, trait-object decoder suitable for Axum state.
151///
152/// Use this with `Arc::new(decoder)` to share a decoder across request handlers.
153pub type Decoder<T> = Arc<dyn JwtDecoder<T> + Send + Sync>;