Skip to main content

rustauth_core/options/
password.rs

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