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