Skip to main content

rustauth_core/options/
email_verification.rs

1use std::fmt;
2use std::sync::Arc;
3
4use http::Request;
5
6use time::Duration;
7
8use crate::db::User;
9use crate::error::RustAuthError;
10use crate::outbound::OutboundSendFuture;
11
12/// Payload passed to an email verification sender.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct VerificationEmail {
15    pub user: User,
16    pub url: String,
17    pub token: String,
18}
19
20/// Email verification sender hook.
21///
22/// Return an outbound future; RustAuth dispatches it in the background so HTTP
23/// responses do not wait for provider I/O.
24pub trait SendVerificationEmail: Send + Sync + 'static {
25    fn send_verification_email(
26        &self,
27        email: VerificationEmail,
28        request: Option<&Request<Vec<u8>>>,
29    ) -> OutboundSendFuture;
30}
31
32impl<F> SendVerificationEmail for F
33where
34    F: for<'a> Fn(VerificationEmail, Option<&'a Request<Vec<u8>>>) -> OutboundSendFuture
35        + Send
36        + Sync
37        + 'static,
38{
39    fn send_verification_email(
40        &self,
41        email: VerificationEmail,
42        request: Option<&Request<Vec<u8>>>,
43    ) -> OutboundSendFuture {
44        self(email, request)
45    }
46}
47
48/// Payload passed to email verification lifecycle callbacks.
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct EmailVerificationCallbackPayload {
51    pub user: User,
52}
53
54/// Hook invoked before an email is marked as verified or changed.
55pub trait BeforeEmailVerification: Send + Sync + 'static {
56    fn before_email_verification(
57        &self,
58        payload: EmailVerificationCallbackPayload,
59        request: Option<&Request<Vec<u8>>>,
60    ) -> Result<(), RustAuthError>;
61}
62
63impl<F> BeforeEmailVerification for F
64where
65    F: for<'a> Fn(
66            EmailVerificationCallbackPayload,
67            Option<&'a Request<Vec<u8>>>,
68        ) -> Result<(), RustAuthError>
69        + Send
70        + Sync
71        + 'static,
72{
73    fn before_email_verification(
74        &self,
75        payload: EmailVerificationCallbackPayload,
76        request: Option<&Request<Vec<u8>>>,
77    ) -> Result<(), RustAuthError> {
78        self(payload, request)
79    }
80}
81
82/// Hook invoked after an email is marked as verified or changed.
83pub trait AfterEmailVerification: Send + Sync + 'static {
84    fn after_email_verification(
85        &self,
86        payload: EmailVerificationCallbackPayload,
87        request: Option<&Request<Vec<u8>>>,
88    ) -> Result<(), RustAuthError>;
89}
90
91impl<F> AfterEmailVerification for F
92where
93    F: for<'a> Fn(
94            EmailVerificationCallbackPayload,
95            Option<&'a Request<Vec<u8>>>,
96        ) -> Result<(), RustAuthError>
97        + Send
98        + Sync
99        + 'static,
100{
101    fn after_email_verification(
102        &self,
103        payload: EmailVerificationCallbackPayload,
104        request: Option<&Request<Vec<u8>>>,
105    ) -> Result<(), RustAuthError> {
106        self(payload, request)
107    }
108}
109
110/// Email verification configuration.
111#[derive(Clone, Default)]
112pub struct EmailVerificationOptions {
113    pub send_verification_email: Option<Arc<dyn SendVerificationEmail>>,
114    pub before_email_verification: Option<Arc<dyn BeforeEmailVerification>>,
115    pub after_email_verification: Option<Arc<dyn AfterEmailVerification>>,
116    pub expires_in: Option<Duration>,
117    pub send_on_sign_up: bool,
118    pub send_on_sign_in: bool,
119    pub auto_sign_in_after_verification: bool,
120}
121
122impl EmailVerificationOptions {
123    pub fn new() -> Self {
124        Self::default()
125    }
126
127    pub fn builder() -> Self {
128        Self::new()
129    }
130
131    #[must_use]
132    pub fn send_verification_email<S>(mut self, sender: S) -> Self
133    where
134        S: SendVerificationEmail,
135    {
136        self.send_verification_email = Some(Arc::new(sender));
137        self
138    }
139
140    #[must_use]
141    pub fn before_email_verification<B>(mut self, callback: B) -> Self
142    where
143        B: BeforeEmailVerification,
144    {
145        self.before_email_verification = Some(Arc::new(callback));
146        self
147    }
148
149    #[must_use]
150    pub fn after_email_verification<A>(mut self, callback: A) -> Self
151    where
152        A: AfterEmailVerification,
153    {
154        self.after_email_verification = Some(Arc::new(callback));
155        self
156    }
157
158    #[must_use]
159    pub fn expires_in(mut self, expires_in: Duration) -> Self {
160        self.expires_in = Some(expires_in);
161        self
162    }
163
164    #[must_use]
165    pub fn send_on_sign_up(mut self, enabled: bool) -> Self {
166        self.send_on_sign_up = enabled;
167        self
168    }
169
170    #[must_use]
171    pub fn send_on_sign_in(mut self, enabled: bool) -> Self {
172        self.send_on_sign_in = enabled;
173        self
174    }
175
176    #[must_use]
177    pub fn auto_sign_in_after_verification(mut self, enabled: bool) -> Self {
178        self.auto_sign_in_after_verification = enabled;
179        self
180    }
181}
182
183impl fmt::Debug for EmailVerificationOptions {
184    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
185        formatter
186            .debug_struct("EmailVerificationOptions")
187            .field(
188                "send_verification_email",
189                &self
190                    .send_verification_email
191                    .as_ref()
192                    .map(|_| "<send-verification-email>"),
193            )
194            .field(
195                "before_email_verification",
196                &self
197                    .before_email_verification
198                    .as_ref()
199                    .map(|_| "<before-email-verification>"),
200            )
201            .field(
202                "after_email_verification",
203                &self
204                    .after_email_verification
205                    .as_ref()
206                    .map(|_| "<after-email-verification>"),
207            )
208            .field("expires_in", &self.expires_in)
209            .field("send_on_sign_up", &self.send_on_sign_up)
210            .field("send_on_sign_in", &self.send_on_sign_in)
211            .field(
212                "auto_sign_in_after_verification",
213                &self.auto_sign_in_after_verification,
214            )
215            .finish()
216    }
217}