Skip to main content

alibi_api/plugins/email_password/
config.rs

1use super::*;
2#[derive(Clone)]
3#[expect(
4    clippy::struct_excessive_bools,
5    reason = "Independent configuration switches model distinct upstream behavior, rather than mutually exclusive states"
6)]
7pub struct EmailPasswordConfig {
8    /// Whether email/password authentication is enabled. Routes remain registered.
9    pub enabled: bool,
10    pub enable_signup: bool,
11    /// Whether to enable the username schema, signup hooks, and endpoints.
12    pub enable_username: bool,
13    pub username: UsernameConfig,
14    pub require_email_verification: bool,
15    /// Minimum UTF-16 password length. Zero uses the default of 8.
16    pub password_min_length: usize,
17    /// Maximum UTF-16 password length. Zero uses the default of 128.
18    pub password_max_length: usize,
19    /// Whether to automatically sign in the user after sign-up (default: true).
20    /// When false, sign-up returns the user but doesn't create a session.
21    pub auto_sign_in: bool,
22    /// Custom password hasher. When `None`, the default scrypt hasher is used.
23    pub password_hasher: Option<Arc<dyn PasswordHasher>>,
24    pub on_existing_user_signup: Option<Arc<ExistingUserSignupCallback>>,
25    pub custom_synthetic_user: Option<Arc<CustomSyntheticUserCallback>>,
26}
27
28/// Password length limits in UTF-16 units, shared by every endpoint that
29/// accepts a new password. The email/password plugin is always installed, so
30/// its configuration is the single source; the defaults only apply to contexts
31/// built without it.
32pub(in crate::plugins) fn password_length_limits(
33    ctx: &alibi_core::AuthContext<impl alibi_core::AuthSchema>,
34) -> (usize, usize) {
35    ctx.extensions
36        .get::<EmailPasswordConfig>()
37        .map_or((8, 128), |config| {
38            (config.effective_min_length(), config.effective_max_length())
39        })
40}
41
42impl EmailPasswordConfig {
43    pub(in crate::plugins::email_password) const fn effective_min_length(&self) -> usize {
44        if self.password_min_length == 0 {
45            8
46        } else {
47            self.password_min_length
48        }
49    }
50
51    pub(in crate::plugins::email_password) const fn effective_max_length(&self) -> usize {
52        if self.password_max_length == 0 {
53            128
54        } else {
55            self.password_max_length
56        }
57    }
58}
59
60impl std::fmt::Debug for EmailPasswordConfig {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        f.debug_struct("EmailPasswordConfig")
63            .field("enabled", &self.enabled)
64            .field("enable_signup", &self.enable_signup)
65            .field("enable_username", &self.enable_username)
66            .field("username", &self.username)
67            .field(
68                "require_email_verification",
69                &self.require_email_verification,
70            )
71            .field("password_min_length", &self.password_min_length)
72            .field("password_max_length", &self.password_max_length)
73            .field("auto_sign_in", &self.auto_sign_in)
74            .field(
75                "password_hasher",
76                &self.password_hasher.as_ref().map(|_| "custom"),
77            )
78            .field(
79                "on_existing_user_signup",
80                &self.on_existing_user_signup.as_ref().map(|_| "custom"),
81            )
82            .field(
83                "custom_synthetic_user",
84                &self.custom_synthetic_user.as_ref().map(|_| "custom"),
85            )
86            .finish()
87    }
88}
89
90impl Default for EmailPasswordConfig {
91    fn default() -> Self {
92        Self {
93            enabled: true,
94            enable_signup: true,
95            enable_username: true,
96            username: UsernameConfig::default(),
97            require_email_verification: false,
98            password_min_length: 8,
99            password_max_length: 128,
100            auto_sign_in: true,
101            password_hasher: None,
102            on_existing_user_signup: None,
103            custom_synthetic_user: None,
104        }
105    }
106}