Skip to main content

openauth_core/options/
password.rs

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