axum_oidc_layer/
lib.rs

1//! Axum OIDC Authentication Layer
2//!
3//! This crate provides a configurable OIDC (OpenID Connect) authentication layer for Axum applications.
4//! It supports JWT token validation with caching for improved performance and includes pluggable cache backends.
5//!
6//! # Features
7//!
8//! - **Automatic Token Validation**: Validates JWT tokens against OIDC provider's JWKS
9//! - **Multi-tier Caching**: Caches OIDC configuration, individual JWK keys, and token validation results
10//! - **Type-safe Cache Keys**: Prevents mixing different cache key types
11//! - **Pluggable Cache Backends**: Implement custom cache strategies (Redis, database, etc.)
12//! - **Configurable TTL**: Control cache lifetimes for different data types
13//! - **Proper Error Handling**: Comprehensive error types with appropriate HTTP responses
14//!
15//! # Quick Start
16//!
17//! ```rust,ignore
18//! use axum_oidc_layer::{OidcAuthenticationLayer, AuthenticationConfigProvider, Claims};
19//! use std::time::Duration;
20//!
21//! #[derive(Clone)]
22//! struct MyConfig;
23//!
24//! impl AuthenticationConfigProvider for MyConfig {
25//!     fn get_provider_url(&self) -> String {
26//!         "https://your-oidc-provider.com".to_string()
27//!     }
28//!     
29//!     fn get_openid_configuration_url(&self) -> Option<String> {
30//!         None // Uses default /.well-known/openid-configuration
31//!     }
32//! }
33//!
34//! let layer = OidcAuthenticationLayer::<MyConfig, Claims>::new(MyConfig);
35//! ```
36
37use axum_core::{
38    extract::FromRequestParts,
39    response::{IntoResponse, Response},
40};
41use http::request::Parts;
42use reqwest::StatusCode;
43
44// Module declarations
45pub mod cache;
46pub mod config;
47pub mod error;
48pub mod jwks;
49pub mod layer;
50pub mod token;
51pub mod validation;
52
53// Public re-exports for convenience
54pub use cache::{ConfigCacheKey, InMemoryCache, JwkCacheKey, JwksCache, TokenCacheKey};
55pub use config::{AuthenticationConfigProvider, OidcConfiguration};
56pub use error::OidcError;
57pub use layer::{OidcAuthenticationLayer, OidcAuthenticationService};
58
59/// Default JWT claims structure.
60///
61/// This provides a basic claims structure that can be used out-of-the-box.
62/// For custom claims, implement your own struct that derives `serde::Deserialize` and `serde::Serialize`.
63#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
64pub struct Claims {
65    /// Subject (user identifier)
66    pub sub: String,
67}
68
69impl<S: Sync> FromRequestParts<S> for Claims {
70    type Rejection = AuthenticationError;
71
72    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
73        parts
74            .extensions
75            .get::<Self>()
76            .cloned()
77            .ok_or(AuthenticationError::InvalidToken)
78    }
79}
80
81/// Legacy authentication error type for backward compatibility.
82///
83/// For new code, prefer using `OidcError` directly.
84#[derive(Debug, Clone)]
85pub enum AuthenticationError {
86    /// Token is invalid or malformed
87    InvalidToken,
88    /// Authorization header is missing
89    MissingToken,
90}
91
92impl std::fmt::Display for AuthenticationError {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        match self {
95            Self::InvalidToken => write!(f, "Invalid token"),
96            Self::MissingToken => write!(f, "Missing token"),
97        }
98    }
99}
100
101impl IntoResponse for AuthenticationError {
102    fn into_response(self) -> axum_core::response::Response {
103        let (status, message) = match self {
104            Self::InvalidToken => {
105                (StatusCode::UNAUTHORIZED, "Invalid authorization token")
106            }
107            Self::MissingToken => {
108                (StatusCode::UNAUTHORIZED, "Missing authorization token")
109            }
110        };
111
112        Response::builder()
113            .status(status)
114            .body(axum_core::body::Body::from(message))
115            .unwrap_or_default()
116    }
117}
118
119impl std::error::Error for AuthenticationError {}