torii_axum/lib.rs
1//! # Torii Axum Integration
2//!
3//! This crate provides Axum routes and middleware for the Torii authentication framework.
4//! It offers a simple way to add authentication to your Axum application with support for
5//! multiple authentication methods.
6//!
7//! ## Features
8//!
9//! - **Core Authentication**: Session management, user info, health checks
10//! - **Password Authentication** (feature = "password"): Registration, login, password changes
11//! - **Magic Link Authentication** (feature = "magic-link"): Passwordless email authentication
12//! - **OAuth** (feature = "oauth"): Coming soon
13//! - **Passkey** (feature = "passkey"): Coming soon
14//!
15//! ## Example Usage
16//!
17//! ```rust,no_run
18//! use std::sync::Arc;
19//! use axum::{Router, routing::get};
20//! use torii::{Torii, SeaORMRepositoryProvider};
21//! use torii_axum::{routes, HasTorii, auth_middleware, CookieConfig};
22//!
23//! // Define your application state with a torii field
24//! #[derive(Clone)]
25//! struct AppState {
26//! torii: Arc<Torii<SeaORMRepositoryProvider>>,
27//! // Add other state fields as needed
28//! // database: Arc<sqlx::PgPool>,
29//! }
30//!
31//! // Implement HasTorii for your state
32//! impl HasTorii<SeaORMRepositoryProvider> for AppState {
33//! fn torii(&self) -> &Arc<Torii<SeaORMRepositoryProvider>> {
34//! &self.torii
35//! }
36//! }
37//!
38//! #[tokio::main]
39//! async fn main() {
40//! // Set up Torii with your storage backend
41//! let repositories = Arc::new(SeaORMRepositoryProvider::new(pool));
42//! let torii = Arc::new(Torii::new(repositories));
43//!
44//! // Create your application state
45//! let state = AppState { torii: torii.clone() };
46//!
47//! // Create auth routes with custom cookie configuration
48//! let auth_routes = routes(torii)
49//! .with_cookie_config(CookieConfig::development())
50//! .build();
51//!
52//! // Create your application router
53//! let app = Router::new()
54//! .nest("/auth", auth_routes)
55//! .route("/protected", get(protected_handler))
56//! .with_state(state.clone())
57//! .layer(axum::middleware::from_fn_with_state(
58//! state,
59//! auth_middleware::<AppState, SeaORMRepositoryProvider>
60//! ));
61//!
62//! // Run your server
63//! let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
64//! axum::serve(listener, app).await.unwrap();
65//! }
66//!
67//! async fn protected_handler() -> &'static str {
68//! "This route requires authentication!"
69//! }
70//! ```
71
72mod error;
73mod extractors;
74mod middleware;
75mod routes;
76mod types;
77
78pub use error::{AuthError, Result};
79pub use extractors::{
80 AuthUser, OptionalAuthUser, SessionTokenFromBearer, SessionTokenFromCookie,
81 SessionTokenFromRequest,
82};
83pub use middleware::{HasTorii, auth_middleware, require_auth};
84pub use routes::create_router;
85pub use types::{
86 AuthResponse, ChangePasswordRequest, ConnectionInfo, CookieConfig, CookieSameSite,
87 HealthResponse, LinkConfig, LoginRequest, MagicLinkRequest, MagicLinkResponse, MessageResponse,
88 PasswordResetRequest, PasswordResetResponse, RegisterRequest, ResetPasswordRequest,
89 SessionResponse, UserResponse, VerifyMagicTokenRequest, VerifyResetTokenResponse,
90};
91
92use axum::Router;
93use std::sync::Arc;
94use torii::Torii;
95use torii_core::RepositoryProvider;
96
97/// Create authentication routes for your Axum application.
98///
99/// This function creates a router with all available authentication endpoints
100/// based on the features enabled in your Cargo.toml.
101///
102/// # Arguments
103///
104/// * `torii` - An Arc-wrapped Torii instance configured with your storage backend
105///
106/// # Returns
107///
108/// A Router that can be nested into your application at any path (e.g., "/auth")
109///
110/// # Example
111///
112/// ```rust,no_run
113/// let auth_routes = torii_axum::routes(torii);
114/// let app = Router::new().nest("/auth", auth_routes);
115/// ```
116pub fn routes<R>(torii: Arc<Torii<R>>) -> AuthRouterBuilder<R>
117where
118 R: RepositoryProvider + 'static,
119{
120 AuthRouterBuilder {
121 torii,
122 cookie_config: CookieConfig::default(),
123 link_config: None,
124 }
125}
126
127/// Builder for configuring authentication routes
128pub struct AuthRouterBuilder<R: RepositoryProvider> {
129 torii: Arc<Torii<R>>,
130 cookie_config: CookieConfig,
131 link_config: Option<LinkConfig>,
132}
133
134impl<R: RepositoryProvider + 'static> AuthRouterBuilder<R> {
135 /// Set custom cookie configuration
136 pub fn with_cookie_config(mut self, config: CookieConfig) -> Self {
137 self.cookie_config = config;
138 self
139 }
140
141 /// Set link configuration for email verification URLs.
142 ///
143 /// This is required when the `magic-link` or `password` features are enabled.
144 /// The configuration specifies the hostname and path prefix used to construct
145 /// verification URLs sent in emails.
146 ///
147 /// # Example
148 ///
149 /// ```rust,no_run
150 /// use torii_axum::{routes, LinkConfig};
151 ///
152 /// let auth_routes = routes(torii)
153 /// .with_link_config(LinkConfig::new("https://example.com"))
154 /// .build();
155 /// ```
156 pub fn with_link_config(mut self, config: LinkConfig) -> Self {
157 self.link_config = Some(config);
158 self
159 }
160
161 /// Build the router with the configured options.
162 ///
163 /// # Panics
164 ///
165 /// Panics if `magic-link` or `password` features are enabled but `LinkConfig`
166 /// is not provided via `with_link_config()`.
167 pub fn build(self) -> Router {
168 #[cfg(any(feature = "magic-link", feature = "password"))]
169 if self.link_config.is_none() {
170 panic!(
171 "LinkConfig is required when magic-link or password features are enabled. \
172 Use .with_link_config(LinkConfig::new(\"https://example.com\"))"
173 );
174 }
175
176 create_router(self.torii, self.cookie_config, self.link_config)
177 }
178}
179
180impl<R: RepositoryProvider + 'static> From<AuthRouterBuilder<R>> for Router {
181 fn from(builder: AuthRouterBuilder<R>) -> Self {
182 builder.build()
183 }
184}