Skip to main content

better_auth_core/
plugin.rs

1use async_trait::async_trait;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5use crate::adapters::DatabaseAdapter;
6use crate::config::AuthConfig;
7use crate::email::EmailProvider;
8use crate::error::{AuthError, AuthResult};
9use crate::types::{AuthRequest, AuthResponse, HttpMethod, Session, User};
10
11/// Plugin trait that all authentication plugins must implement
12#[async_trait]
13pub trait AuthPlugin: Send + Sync {
14    /// Plugin name - should be unique
15    fn name(&self) -> &'static str;
16
17    /// Routes that this plugin handles
18    fn routes(&self) -> Vec<AuthRoute>;
19
20    /// Called when the plugin is initialized
21    async fn on_init(&self, ctx: &mut AuthContext) -> AuthResult<()> {
22        let _ = ctx;
23        Ok(())
24    }
25
26    /// Called for each request - return Some(response) to handle, None to pass through
27    async fn on_request(
28        &self,
29        req: &AuthRequest,
30        ctx: &AuthContext,
31    ) -> AuthResult<Option<AuthResponse>>;
32
33    /// Called after a user is created
34    async fn on_user_created(&self, user: &User, ctx: &AuthContext) -> AuthResult<()> {
35        let _ = (user, ctx);
36        Ok(())
37    }
38
39    /// Called after a session is created
40    async fn on_session_created(&self, session: &Session, ctx: &AuthContext) -> AuthResult<()> {
41        let _ = (session, ctx);
42        Ok(())
43    }
44
45    /// Called before a user is deleted
46    async fn on_user_deleted(&self, user_id: &str, ctx: &AuthContext) -> AuthResult<()> {
47        let _ = (user_id, ctx);
48        Ok(())
49    }
50
51    /// Called before a session is deleted
52    async fn on_session_deleted(&self, session_token: &str, ctx: &AuthContext) -> AuthResult<()> {
53        let _ = (session_token, ctx);
54        Ok(())
55    }
56}
57
58/// Route definition for plugins
59#[derive(Debug, Clone)]
60pub struct AuthRoute {
61    pub path: String,
62    pub method: HttpMethod,
63    pub handler: String,
64}
65
66/// Context passed to plugin methods
67pub struct AuthContext {
68    pub config: Arc<AuthConfig>,
69    pub database: Arc<dyn DatabaseAdapter>,
70    pub email_provider: Option<Arc<dyn EmailProvider>>,
71    pub metadata: HashMap<String, serde_json::Value>,
72}
73
74impl AuthRoute {
75    pub fn new(method: HttpMethod, path: impl Into<String>, handler: impl Into<String>) -> Self {
76        Self {
77            path: path.into(),
78            method,
79            handler: handler.into(),
80        }
81    }
82
83    pub fn get(path: impl Into<String>, handler: impl Into<String>) -> Self {
84        Self::new(HttpMethod::Get, path, handler)
85    }
86
87    pub fn post(path: impl Into<String>, handler: impl Into<String>) -> Self {
88        Self::new(HttpMethod::Post, path, handler)
89    }
90
91    pub fn put(path: impl Into<String>, handler: impl Into<String>) -> Self {
92        Self::new(HttpMethod::Put, path, handler)
93    }
94
95    pub fn delete(path: impl Into<String>, handler: impl Into<String>) -> Self {
96        Self::new(HttpMethod::Delete, path, handler)
97    }
98}
99
100impl AuthContext {
101    pub fn new(config: Arc<AuthConfig>, database: Arc<dyn DatabaseAdapter>) -> Self {
102        let email_provider = config.email_provider.clone();
103        Self {
104            config,
105            database,
106            email_provider,
107            metadata: HashMap::new(),
108        }
109    }
110
111    pub fn set_metadata(&mut self, key: impl Into<String>, value: serde_json::Value) {
112        self.metadata.insert(key.into(), value);
113    }
114
115    pub fn get_metadata(&self, key: &str) -> Option<&serde_json::Value> {
116        self.metadata.get(key)
117    }
118
119    /// Get the email provider, returning an error if none is configured.
120    pub fn email_provider(&self) -> AuthResult<&dyn EmailProvider> {
121        self.email_provider
122            .as_deref()
123            .ok_or_else(|| AuthError::config("No email provider configured"))
124    }
125}