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::sync::Arc;
64
65use async_trait::async_trait;
66use jsonwebtoken::TokenData;
67use serde::de::DeserializeOwned;
68use thiserror::Error;
69
70pub use crate::axum::{
71 AuthError, BearerTokenExtractor, Claims, CookieTokenExtractor, ExtractorConfig,
72 HeaderTokenExtractor, JwtDecoderState, TokenExtractor,
73};
74pub use crate::local::LocalDecoder;
75pub use crate::remote::{
76 RemoteJwksDecoder, RemoteJwksDecoderBuilder, RemoteJwksDecoderConfig,
77 RemoteJwksDecoderConfigBuilder,
78};
79
80/// Errors that can occur during JWT decoding and validation.
81#[derive(Debug, thiserror::Error)]
82pub enum Error {
83 /// The JWT's `kid` (key ID) was not found in the key cache.
84 #[error("JWT key not found (kid: {0:?})")]
85 KeyNotFound(Option<String>),
86
87 /// Invalid decoder configuration (e.g., missing keys, algorithms, or audience).
88 #[error("Configuration error: {0}")]
89 Configuration(String),
90
91 /// JWT validation or decoding error from the `jsonwebtoken` crate.
92 #[error("JWT error: {0}")]
93 Jwt(#[from] jsonwebtoken::errors::Error),
94
95 /// HTTP request error when fetching remote JWKS.
96 #[error("HTTP request error: {0}")]
97 Reqwest(#[from] reqwest::Error),
98
99 /// JWKS refresh failed after exhausting all retry attempts.
100 #[error("JWKS refresh failed after {retry_count} attempts: {message}")]
101 JwksRefresh {
102 message: String,
103 retry_count: usize,
104 #[source]
105 source: Option<Box<dyn std::error::Error + Send + Sync>>,
106 },
107}
108
109/// Trait for decoding and validating JWT tokens.
110///
111/// Implemented by [`LocalDecoder`] and [`RemoteJwksDecoder`] to provide
112/// a unified interface for JWT validation with different key sources.
113#[async_trait]
114pub trait JwtDecoder<T>
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 async fn decode(&self, token: &str) -> Result<TokenData<T>, Error>;
125}
126
127/// Type alias for a thread-safe, trait-object decoder suitable for Axum state.
128///
129/// Use this with `Arc::new(decoder)` to share a decoder across request handlers.
130pub type Decoder<T> = Arc<dyn JwtDecoder<T> + Send + Sync>;