Skip to main content

better_auth_core/plugin/
initialization.rs

1use super::*;
2pub struct AuthInitParts {
3    pub metadata: MetadataMap,
4    pub email_provider: Option<Arc<dyn EmailProvider>>,
5    pub extensions: ContextExtensions,
6}
7
8/// Initialization context passed to plugin setup.
9pub struct AuthInitContext<S: AuthSchema> {
10    pub config: Arc<AuthConfig>,
11    pub database: Arc<dyn AuthStore<S>>,
12    pub email_provider: Option<Arc<dyn EmailProvider>>,
13    pub metadata: MetadataMap,
14    pub extensions: ContextExtensions,
15}
16
17impl<S: AuthSchema> AuthInitContext<S> {
18    #[must_use]
19    pub fn new(config: Arc<AuthConfig>, database: Arc<dyn AuthStore<S>>) -> Self {
20        let email_provider = config.email_provider.clone();
21        Self {
22            config,
23            database,
24            email_provider,
25            metadata: MetadataMap::new(),
26            extensions: ContextExtensions::default(),
27        }
28    }
29
30    pub fn set_metadata(&mut self, key: impl Into<String>, value: serde_json::Value) {
31        drop(self.metadata.insert(key.into(), value));
32    }
33
34    /// Decorate the instance's actual password hashing boundary. Later
35    /// registrations run first, matching initialized hash wrappers.
36    pub fn register_password_hash_hook(
37        &mut self,
38        hook: Arc<dyn crate::utils::password::PasswordHashHook>,
39    ) {
40        let mut hooks = self
41            .extensions
42            .get::<crate::utils::password::PasswordHashHooks>()
43            .map(|value| (*value).clone())
44            .unwrap_or_default();
45        hooks.0.push(hook);
46        self.extensions.insert(hooks);
47    }
48
49    #[must_use]
50    pub fn get_metadata(&self, key: &str) -> Option<&serde_json::Value> {
51        self.metadata.get(key)
52    }
53
54    /// Register a transform for user creation, including transactional creation.
55    pub fn register_user_create_transform<F>(&mut self, transform: F)
56    where
57        F: Fn(crate::types::CreateUser) -> AuthResult<crate::types::CreateUser>
58            + Send
59            + Sync
60            + 'static,
61    {
62        let mut transforms = self
63            .extensions
64            .get::<crate::store::UserTransforms>()
65            .map(|value| (*value).clone())
66            .unwrap_or_default();
67        transforms.creates.push(Arc::new(transform));
68        self.extensions.insert(transforms);
69    }
70
71    /// Register model defaults applied after creation hooks to an identity
72    /// candidate admitted by the configured user validation policy.
73    pub fn register_user_creation_adapter_default<F>(&mut self, default: F)
74    where
75        F: Fn(crate::types::CreateUser) -> AuthResult<crate::types::CreateUser>
76            + Send
77            + Sync
78            + 'static,
79    {
80        let mut transforms = self
81            .extensions
82            .get::<crate::store::UserTransforms>()
83            .map(|value| (*value).clone())
84            .unwrap_or_default();
85        transforms.adapter_defaults.0.push(Arc::new(default));
86        self.extensions.insert(transforms);
87    }
88
89    /// Register a transform applied to every user update through this auth instance.
90    pub fn register_user_update_transform<F>(&mut self, transform: F)
91    where
92        F: Fn(&str, crate::types::UpdateUser) -> AuthResult<crate::types::UpdateUser>
93            + Send
94            + Sync
95            + 'static,
96    {
97        let mut transforms = self
98            .extensions
99            .get::<crate::store::UserTransforms>()
100            .map(|value| (*value).clone())
101            .unwrap_or_default();
102        transforms.updates.push(Arc::new(transform));
103        self.extensions.insert(transforms);
104    }
105
106    /// Register an adapter lifecycle callback for each committed session creation.
107    /// Transactional creations defer callbacks until commit and discard them on
108    /// rollback. A callback error propagates after persistence, as for other
109    /// adapter after callbacks. Trusted server operations have no request context.
110    pub fn register_session_created_hook(
111        &mut self,
112        callback: Arc<dyn crate::store::SessionCreatedHook<S>>,
113    ) {
114        let mut callbacks = self
115            .extensions
116            .get::<crate::store::SessionCreatedCallbacks<S>>()
117            .map(|value| (*value).clone())
118            .unwrap_or_default();
119        callbacks.callbacks.push(callback);
120        self.extensions.insert(callbacks);
121    }
122
123    /// Observe genuine retained adapter output after a write, with transaction
124    /// callbacks deferred until commit. Existing typed physical hooks are separate.
125    pub fn register_adapter_after_hook(
126        &mut self,
127        callback: Arc<dyn crate::store::AdapterAfterHook<S>>,
128    ) {
129        let mut callbacks = self
130            .extensions
131            .get::<crate::store::AdapterCallbacks<S>>()
132            .map(|callbacks| (*callbacks).clone())
133            .unwrap_or_default();
134        callbacks.0.push(callback);
135        self.extensions.insert(callbacks);
136    }
137
138    /// Finalize the instance's store without mutating a shared underlying adapter.
139    #[must_use]
140    pub fn database_with_registered_transforms(&self) -> Arc<dyn AuthStore<S>> {
141        let transforms = self
142            .extensions
143            .get::<crate::store::UserTransforms>()
144            .map(|value| (*value).clone())
145            .unwrap_or_default();
146        let session_callbacks = self
147            .extensions
148            .get::<crate::store::SessionCreatedCallbacks<S>>()
149            .map(|value| (*value).clone())
150            .unwrap_or_default();
151        let fields = self.extensions.get::<crate::field_policy::SessionFields>();
152        if self.config.session.secondary_storage.is_none()
153            && transforms.creates.is_empty()
154            && transforms.updates.is_empty()
155            && session_callbacks.callbacks.is_empty()
156            && fields.is_none()
157            && self.config.user.additional_fields.is_empty()
158            && self.config.session.additional_fields.is_empty()
159            && self.config.account.additional_fields.is_empty()
160            && self
161                .extensions
162                .get::<crate::store::AdapterCallbacks<S>>()
163                .is_none_or(|callbacks| callbacks.0.is_empty())
164            && self.config.user_validation.is_none()
165        {
166            return Arc::clone(&self.database);
167        }
168        Arc::new(crate::store::PluginStore::new(
169            Arc::clone(&self.database),
170            Arc::clone(&self.config),
171            transforms,
172            session_callbacks,
173            fields.map_or_else(
174                || {
175                    crate::field_policy::SessionFields(
176                        self.config.session.additional_fields.clone(),
177                    )
178                },
179                |fields| (*fields).clone(),
180            ),
181            self.extensions
182                .get::<crate::field_policy::SessionAdapterFields>()
183                .map(|fields_2| (*fields_2).clone())
184                .unwrap_or_else(|| {
185                    crate::field_policy::SessionAdapterFields(Arc::new(
186                        self.config.session.additional_fields.clone(),
187                    ))
188                }),
189            AuthContext::with_metadata(
190                Arc::clone(&self.config),
191                Arc::clone(&self.database),
192                self.metadata.clone(),
193            )
194            .with_extensions(self.extensions.clone()),
195        ))
196    }
197
198    pub fn set_email_verification_override(
199        &mut self,
200        sender: Arc<dyn VerificationEmailOverride<S>>,
201    ) {
202        self.extensions
203            .insert(VerificationEmailOverrideHandle(sender));
204    }
205
206    #[must_use]
207    pub fn into_parts(self) -> AuthInitParts {
208        AuthInitParts {
209            metadata: self.metadata,
210            email_provider: self.email_provider,
211            extensions: self.extensions,
212        }
213    }
214}
215
216impl std::fmt::Debug for AuthInitParts {
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        f.debug_struct("AuthInitParts").finish_non_exhaustive()
219    }
220}
221
222impl<S: AuthSchema> std::fmt::Debug for AuthInitContext<S> {
223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224        f.debug_struct("AuthInitContext").finish_non_exhaustive()
225    }
226}