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}