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>;