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 axum::{Router, routing::get, Json};
19//! use axum_jwt_auth::{Claims, LocalDecoder, JwtDecoderState};
20//! use serde::{Deserialize, Serialize};
21//!
22//! #[derive(Deserialize, Serialize)]
23//! struct MyClaims {
24//!     sub: String,
25//!     exp: usize,
26//! }
27//!
28//! async fn protected_handler(user: Claims<MyClaims>) -> Json<MyClaims> {
29//!     Json(user.claims)
30//! }
31//! ```
32//!
33//! ## Custom Token Extractors
34//!
35//! Use macros to easily define custom extractors:
36//!
37//! ```ignore
38//! use axum_jwt_auth::{define_header_extractor, define_cookie_extractor};
39//! use axum_jwt_auth::{Claims, HeaderTokenExtractor, CookieTokenExtractor};
40//!
41//! // Define custom extractors
42//! define_header_extractor!(XAuthToken, "x-auth-token");
43//! define_cookie_extractor!(AuthCookie, "auth_token");
44//!
45//! // Use in handlers
46//! async fn header_handler(user: Claims<MyClaims, HeaderTokenExtractor<XAuthToken>>) {
47//!     // Token extracted from "x-auth-token" header
48//! }
49//!
50//! async fn cookie_handler(user: Claims<MyClaims, CookieTokenExtractor<AuthCookie>>) {
51//!     // Token extracted from "auth_token" cookie
52//! }
53//! ```
54//!
55//! # Examples
56//!
57//! For full examples, see the [examples directory](https://github.com/cmackenzie1/axum-jwt-auth/blob/main/examples).
58
59mod axum;
60mod local;
61mod remote;
62
63use std::future::Future;
64use std::pin::Pin;
65use std::sync::Arc;
66
67use jsonwebtoken::TokenData;
68use serde::de::DeserializeOwned;
69use thiserror::Error;
70
71pub use crate::axum::{
72    AuthError, BearerTokenExtractor, Claims, CookieTokenExtractor, ExtractorConfig,
73    HeaderTokenExtractor, JwtDecoderState, TokenExtractor,
74};
75pub use crate::local::LocalDecoder;
76pub use crate::remote::{
77    RemoteJwksDecoder, RemoteJwksDecoderBuilder, RemoteJwksDecoderConfig,
78    RemoteJwksDecoderConfigBuilder,
79};
80
81/// Errors that can occur during JWT decoding and validation.
82#[derive(Debug, thiserror::Error)]
83pub enum Error {
84    /// The JWT's `kid` (key ID) was not found in the key cache.
85    #[error("JWT key not found (kid: {0:?})")]
86    KeyNotFound(Option<String>),
87
88    /// Invalid decoder configuration (e.g., missing keys, algorithms, or audience).
89    #[error("Configuration error: {0}")]
90    Configuration(String),
91
92    /// JWT validation or decoding error from the `jsonwebtoken` crate.
93    #[error("JWT error: {0}")]
94    Jwt(#[from] jsonwebtoken::errors::Error),
95
96    /// HTTP request error when fetching remote JWKS.
97    #[error("HTTP request error: {0}")]
98    Reqwest(#[from] reqwest::Error),
99
100    /// JWKS refresh failed after exhausting all retry attempts.
101    #[error("JWKS refresh failed after {retry_count} attempts: {message}")]
102    JwksRefresh {
103        message: String,
104        retry_count: usize,
105        #[source]
106        source: Option<Box<dyn std::error::Error + Send + Sync>>,
107    },
108}
109
110/// Trait for decoding and validating JWT tokens.
111///
112/// Implemented by [`LocalDecoder`] and [`RemoteJwksDecoder`] to provide
113/// a unified interface for JWT validation with different key sources.
114pub trait JwtDecoder<T>: Send + Sync
115where
116    T: for<'de> DeserializeOwned,
117{
118    /// Decodes and validates a JWT token string, returning the parsed claims.
119    ///
120    /// # Errors
121    ///
122    /// Returns an error if the token is invalid, expired, has an invalid signature,
123    /// or if the key cannot be found (for remote decoders).
124    fn decode<'a>(
125        &'a self,
126        token: &'a str,
127    ) -> Pin<Box<dyn Future<Output = Result<TokenData<T>, Error>> + Send + 'a>>;
128}
129
130/// Type alias for a thread-safe, trait-object decoder suitable for Axum state.
131///
132/// Use this with `Arc::new(decoder)` to share a decoder across request handlers.
133pub type Decoder<T> = Arc<dyn JwtDecoder<T> + Send + Sync>;