axum_gate/gate/
mod.rs

1//! Gate implementation for protecting axum routes with JWT authentication.
2//!
3//! The `Gate` provides a high-level API for adding authentication and authorization
4//! to your axum routes using JWT cookies or bearer tokens. It supports role-based
5//! access control, group-based access control, and fine-grained permission systems.
6//!
7//! # Basic Usage
8//!
9//! ```rust
10//! use axum::{routing::get, Router};
11//! use axum_gate::prelude::*;
12//! use std::sync::Arc;
13//!
14//! # async fn protected_handler() -> &'static str { "Protected!" }
15//! let jwt_codec = Arc::new(JsonWebToken::<JwtClaims<Account<Role, Group>>>::default());
16//! let cookie_template = CookieTemplate::recommended()
17//!     .name("auth-token")
18//!     .persistent(cookie::time::Duration::hours(24));
19//!
20//! let app = Router::<()>::new()
21//!     .route("/admin", get(protected_handler))
22//!     .layer(
23//!         Gate::cookie("my-app", jwt_codec)
24//!             .with_policy(AccessPolicy::<Role, Group>::require_role(Role::Admin))
25//!             .with_cookie_template(cookie_template)
26//!     );
27//! ```
28//!
29//! # Access Control Examples
30//!
31//! ## Role-Based Access
32//! ```rust
33//! # use axum_gate::prelude::*;
34//! # use std::sync::Arc;
35//! # let jwt_codec = Arc::new(JsonWebToken::<JwtClaims<Account<Role, Group>>>::default());
36//! // Allow only Admin role
37//! let gate = Gate::cookie("my-app", Arc::clone(&jwt_codec))
38//!     .with_policy(AccessPolicy::<Role, Group>::require_role(Role::Admin));
39//!
40//! // Allow Admin or Moderator roles
41//! let gate = Gate::cookie("my-app", Arc::clone(&jwt_codec))
42//!     .with_policy(
43//!         AccessPolicy::<Role, Group>::require_role(Role::Admin)
44//!             .or_require_role(Role::Moderator)
45//!     );
46//! ```
47//!
48//! ## Hierarchical Access
49//! ```rust
50//! # use axum_gate::prelude::*;
51//! # use std::sync::Arc;
52//! # let jwt_codec = Arc::new(JsonWebToken::<JwtClaims<Account<Role, Group>>>::default());
53//! // Allow User role and all supervisor roles (Reporter, Moderator, Admin)
54//! let gate = Gate::cookie("my-app", jwt_codec)
55//!     .with_policy(AccessPolicy::<Role, Group>::require_role_or_supervisor(Role::User));
56//! ```
57//!
58//! ## Permission-Based Access
59//! ```rust
60//! # use axum_gate::prelude::*;
61//! # use std::sync::Arc;
62//! # let jwt_codec = Arc::new(JsonWebToken::<JwtClaims<Account<Role, Group>>>::default());
63//! let gate = Gate::cookie("my-app", jwt_codec)
64//!     .with_policy(
65//!         AccessPolicy::<Role, Group>::require_permission(PermissionId::from("read:api"))
66//!     );
67//! ```
68//! ## Bearer Gate (JWT)
69//! Strict bearer (JWT) example:
70//! ```rust
71//! # use axum::{routing::get, Router};
72//! # use axum_gate::prelude::*;
73//! # use std::sync::Arc;
74//! # async fn handler() {}
75//! let jwt = Arc::new(JsonWebToken::<JwtClaims<Account<Role, Group>>>::default());
76//! let app = Router::<()>::new()
77//!     .route("/admin", get(handler))
78//!     .layer(
79//!         Gate::bearer("my-app", Arc::clone(&jwt))
80//!             .with_policy(AccessPolicy::<Role, Group>::require_role(Role::Admin))
81//!     );
82//! ```
83//!
84//! Optional user context (never blocks; handlers must enforce access):
85//! ```rust
86//! # use axum_gate::prelude::*;
87//! # use std::sync::Arc;
88//! let jwt = Arc::new(JsonWebToken::<JwtClaims<Account<Role, Group>>>::default());
89//! let gate = Gate::bearer::<JsonWebToken<JwtClaims<Account<Role, Group>>>, Role, Group>("my-app", jwt).allow_anonymous_with_optional_user();
90//! // Inserts Option<Account<Role, Group>> and Option<RegisteredClaims> into request extensions.
91//! ```
92//!
93use self::bearer::BearerGate;
94use self::cookie::CookieGate;
95use self::oauth2::OAuth2Gate;
96use crate::accounts::Account;
97use crate::authz::AccessHierarchy;
98use crate::codecs::Codec;
99use crate::codecs::jwt::JwtClaims;
100
101use std::sync::Arc;
102
103pub mod bearer;
104pub mod cookie;
105pub mod oauth2;
106
107/// Main entry point for creating authentication gates.
108///
109/// Gates protect your axum routes from unauthorized access using JWT tokens.
110/// All requests are denied by default unless explicitly granted access through
111/// an access policy. Choose between cookie-based gates for web applications
112/// and bearer token gates for APIs and SPAs.
113#[derive(Clone)]
114pub struct Gate;
115
116impl Gate {
117    /// Creates a new cookie-based gate that denies all access by default.
118    ///
119    /// Use this for web applications where you want automatic token handling
120    /// through HTTP-only cookies. Cookie gates provide CSRF protection and
121    /// work seamlessly with browser-based authentication flows.
122    ///
123    /// Attach an access policy using `with_policy()` to grant access. This secure-by-default
124    /// approach ensures no routes are exposed until you explicitly configure a policy.
125    ///
126    /// # Arguments
127    /// * `issuer` - The JWT issuer identifier for your application
128    /// * `codec` - JWT codec for encoding/decoding tokens
129    ///
130    /// # Example
131    /// ```rust
132    /// # use axum_gate::prelude::*;
133    /// # use std::sync::Arc;
134    /// let jwt_codec = Arc::new(JsonWebToken::<JwtClaims<Account<Role, Group>>>::default());
135    /// let policy = AccessPolicy::<Role, Group>::require_role(Role::Admin);
136    ///
137    /// let gate = Gate::cookie("my-app", jwt_codec)
138    ///     .with_policy(policy);
139    /// ```
140    pub fn cookie<C, R, G>(issuer: &str, codec: Arc<C>) -> CookieGate<C, R, G>
141    where
142        C: Codec,
143        R: AccessHierarchy + Eq + std::fmt::Display,
144        G: Eq,
145    {
146        CookieGate::new_with_codec(issuer, codec)
147    }
148
149    /// Creates a new bearer-header based gate that denies all access by default.
150    ///
151    /// Use this for APIs, SPAs, and mobile applications where you need explicit
152    /// token management. Bearer token gates require clients to include tokens
153    /// in the `Authorization: Bearer <token>` header, providing fine-grained
154    /// control over token lifecycle and excellent support for API integrations.
155    ///
156    /// This variant protects routes by expecting an `Authorization: Bearer <token>`
157    /// header. Missing or invalid bearer tokens result in `401 Unauthorized`.
158    ///
159    /// Optional mode is supported via `allow_anonymous_with_optional_user()`. In optional mode,
160    /// requests are always forwarded and the layer inserts `Option<Account<R, G>>` and
161    /// `Option<RegisteredClaims>` (Some only when the token is valid). You can also transition to
162    /// a static shared-secret mode via `.with_static_token("...")`.
163    ///
164    /// # Arguments
165    /// * `issuer` - The JWT issuer identifier for your application
166    /// * `codec` - JWT codec for encoding/decoding tokens
167    pub fn bearer<C, R, G>(
168        issuer: &str,
169        codec: Arc<C>,
170    ) -> BearerGate<C, R, G, bearer::JwtConfig<R, G>>
171    where
172        C: Codec,
173        R: AccessHierarchy + Eq + std::fmt::Display,
174        G: Eq + Clone,
175    {
176        // Delegates to the BearerGate builder (to be implemented in bearer module).
177        BearerGate::new_with_codec(issuer, codec)
178    }
179
180    /// Creates a new OAuth2-based gate builder using the `oauth2` crate.
181    ///
182    /// This returns an OAuth2 flow builder that can mount `/login` and `/callback` routes and,
183    /// on successful callback, will mint a first-party JWT via the existing CookieGate.
184    pub fn oauth2<R, G>() -> OAuth2Gate<R, G>
185    where
186        R: AccessHierarchy + Eq + std::fmt::Display + Send + Sync + 'static,
187        G: Eq + Clone + Send + Sync + 'static,
188    {
189        OAuth2Gate::new()
190    }
191
192    /// Creates a new OAuth2-based gate builder preconfigured with a JWT encoder.
193    ///
194    /// # Arguments
195    /// * `issuer` - JWT issuer for your application
196    /// * `codec` - JWT codec used to mint tokens for the first‑party cookie
197    /// * `ttl_secs` - Expiration (seconds) for issued JWTs
198    pub fn oauth2_with_jwt<C, R, G>(issuer: &str, codec: Arc<C>, ttl_secs: u64) -> OAuth2Gate<R, G>
199    where
200        C: Codec<Payload = JwtClaims<Account<R, G>>> + Send + Sync + 'static,
201        R: AccessHierarchy + Eq + std::fmt::Display + Send + Sync + 'static,
202        G: Eq + Clone + Send + Sync + 'static,
203    {
204        OAuth2Gate::new().with_jwt_codec(issuer, codec, ttl_secs)
205    }
206}