better_auth_api/plugins/email_otp/
callbacks.rs1use super::{EmailOtpMessage, EmailOtpPlugin, EmailOtpType};
2use crate::plugins::endpoint_context::{EndpointContext, WithCallbacks};
3use better_auth_core::background::{BackgroundFuture, run_or_await};
4use better_auth_core::{AuthPlugin, AuthResult, AuthSchema};
5use std::sync::Arc;
6
7type Sender<S> = dyn Fn(&EmailOtpMessage, &EndpointContext<'_, S>) -> AuthResult<Option<BackgroundFuture>>
8 + Send
9 + Sync;
10type Generator<S> =
11 dyn Fn(&str, EmailOtpType, &EndpointContext<'_, S>) -> AuthResult<Option<String>> + Send + Sync;
12
13pub struct EmailOtpCallbacks<S: AuthSchema> {
15 pub(super) sender: Option<Arc<Sender<S>>>,
16 pub(super) generator: Option<Arc<Generator<S>>>,
17}
18impl<S: AuthSchema> Default for EmailOtpCallbacks<S> {
19 fn default() -> Self {
20 Self {
21 sender: None,
22 generator: None,
23 }
24 }
25}
26impl<S: AuthSchema> EmailOtpCallbacks<S> {
27 pub fn send<F>(mut self, callback: F) -> Self
30 where
31 F: Fn(&EmailOtpMessage, &EndpointContext<'_, S>) -> AuthResult<Option<BackgroundFuture>>
32 + Send
33 + Sync
34 + 'static,
35 {
36 self.sender = Some(Arc::new(callback));
37 self
38 }
39 pub fn generate<F>(mut self, callback: F) -> Self
41 where
42 F: Fn(&str, EmailOtpType, &EndpointContext<'_, S>) -> AuthResult<Option<String>>
43 + Send
44 + Sync
45 + 'static,
46 {
47 self.generator = Some(Arc::new(callback));
48 self
49 }
50}
51impl EmailOtpPlugin {
52 pub fn callbacks<S: AuthSchema>(self, callbacks: EmailOtpCallbacks<S>) -> impl AuthPlugin<S> {
54 WithCallbacks {
55 plugin: self,
56 callbacks: Arc::new(callbacks),
57 }
58 }
59}
60
61pub(crate) fn overrides_verification(ctx: &better_auth_core::AuthContext<impl AuthSchema>) -> bool {
63 ctx.extensions
64 .get::<super::EmailOtpConfig>()
65 .is_some_and(|config| config.override_default_email_verification)
66}
67
68pub(crate) async fn send_verification_override(
69 email: &str,
70 endpoint: &EndpointContext<'_, impl AuthSchema>,
71) -> AuthResult<()> {
72 let ctx = endpoint.auth;
73 let config = ctx
74 .extensions
75 .get::<super::EmailOtpConfig>()
76 .ok_or_else(|| {
77 better_auth_core::AuthError::config("Email OTP override is not configured")
78 })?;
79 let plugin = EmailOtpPlugin::with_config(config.clone());
80 let email = email.to_lowercase();
81 let kind = EmailOtpType::EmailVerification;
82 let owned = endpoint.to_owned();
83 let request_context =
84 better_auth_core::hooks::current_request_hook_context().map(|mut context| {
85 context.path = Some("/email-otp/send-verification-otp".into());
86 context.body = better_auth_core::FieldMap::from([
87 ("email".into(), email.clone().into()),
88 ("type".into(), "email-verification".into()),
89 ])
90 .into();
91 context.params.clear();
92 context
93 });
94 let task = Box::pin(async move {
95 let operation = async {
96 let mut endpoint = owned.as_endpoint();
97 endpoint.body = better_auth_core::FieldMap::from([
98 ("email".into(), email.clone().into()),
99 ("type".into(), "email-verification".into()),
100 ])
101 .into();
102 endpoint.path = Some("/email-otp/send-verification-otp");
103 endpoint.session = None;
105 endpoint.response = None;
106 endpoint.params.clear();
107 let otp = plugin.resolve_otp(&endpoint, &email, kind).await?;
108 let user = match endpoint.transaction {
109 Some(transaction) => transaction.get_user_by_email(&email).await?,
110 None => endpoint.auth.database.get_user_by_email(&email).await?,
111 };
112 if user.is_none() {
113 let identifier = kind.identifier(&email);
114 match endpoint.transaction {
115 Some(transaction) => {
116 transaction
117 .delete_verification_by_identifier(&identifier)
118 .await?
119 }
120 None => {
121 endpoint
122 .auth
123 .database
124 .delete_verification_by_identifier(&identifier)
125 .await?
126 }
127 }
128 return Ok(());
129 }
130 plugin.deliver(&endpoint, &email, otp, kind).await
131 };
132 match request_context {
133 Some(context) => {
134 better_auth_core::hooks::with_request_hook_context_value(context, operation).await
135 }
136 None => operation.await,
137 }
138 });
139 run_or_await(
141 Some(task),
142 ctx.config.advanced.background_tasks.as_ref(),
143 &ctx.config.logger,
144 )
145 .await;
146 Ok(())
147}