Skip to main content

rustauth_plugins/last_login_method/
config.rs

1use std::sync::Arc;
2use time::Duration;
3
4use super::resolve::LoginMethodContext;
5
6pub const DEFAULT_COOKIE_NAME: &str = "better-auth.last_used_login_method";
7pub const DEFAULT_COOKIE_MAX_AGE: Duration = Duration::days(30);
8pub const DEFAULT_DATABASE_FIELD_NAME: &str = "last_login_method";
9
10type LoginMethodResolver =
11    Arc<dyn Fn(&LoginMethodContext) -> Option<String> + Send + Sync + 'static>;
12
13/// Configuration for tracking the most recent successful login method.
14#[derive(Clone, Default)]
15pub struct LastLoginMethodOptions {
16    pub cookie_name: Option<String>,
17    pub max_age: Option<Duration>,
18    pub resolver: Option<LoginMethodResolver>,
19    pub store_in_database: bool,
20    pub database_field_name: Option<String>,
21}
22
23impl LastLoginMethodOptions {
24    #[must_use]
25    pub fn builder() -> LastLoginMethodOptionsBuilder {
26        LastLoginMethodOptionsBuilder::default()
27    }
28
29    pub fn cookie_name(mut self, cookie_name: impl Into<String>) -> Self {
30        self.cookie_name = Some(cookie_name.into());
31        self
32    }
33
34    pub fn max_age(mut self, max_age: Duration) -> Self {
35        self.max_age = Some(max_age);
36        self
37    }
38
39    pub fn with_resolver<F>(mut self, resolver: F) -> Self
40    where
41        F: Fn(&LoginMethodContext) -> Option<String> + Send + Sync + 'static,
42    {
43        self.resolver = Some(Arc::new(resolver));
44        self
45    }
46
47    pub fn store_in_database(mut self, store_in_database: bool) -> Self {
48        self.store_in_database = store_in_database;
49        self
50    }
51
52    pub fn database_field_name(mut self, field_name: impl Into<String>) -> Self {
53        self.database_field_name = Some(field_name.into());
54        self
55    }
56
57    pub fn effective_cookie_name(&self) -> &str {
58        self.cookie_name.as_deref().unwrap_or(DEFAULT_COOKIE_NAME)
59    }
60
61    pub fn effective_max_age(&self) -> u64 {
62        self.max_age
63            .unwrap_or(DEFAULT_COOKIE_MAX_AGE)
64            .whole_seconds() as u64
65    }
66
67    pub fn effective_database_field_name(&self) -> &str {
68        self.database_field_name
69            .as_deref()
70            .unwrap_or(DEFAULT_DATABASE_FIELD_NAME)
71    }
72
73    pub fn resolve_login_method(&self, context: &LoginMethodContext) -> Option<String> {
74        self.resolver
75            .as_ref()
76            .and_then(|resolver| resolver(context))
77            .or_else(|| super::resolve::default_login_method(context))
78    }
79}
80
81#[derive(Clone, Default)]
82pub struct LastLoginMethodOptionsBuilder {
83    cookie_name: Option<String>,
84    max_age: Option<Duration>,
85    resolver: Option<LoginMethodResolver>,
86    store_in_database: Option<bool>,
87    database_field_name: Option<String>,
88}
89
90impl LastLoginMethodOptionsBuilder {
91    #[must_use]
92    pub fn cookie_name(mut self, cookie_name: impl Into<String>) -> Self {
93        self.cookie_name = Some(cookie_name.into());
94        self
95    }
96
97    #[must_use]
98    pub fn max_age(mut self, max_age: Duration) -> Self {
99        self.max_age = Some(max_age);
100        self
101    }
102
103    #[must_use]
104    pub fn with_resolver<F>(mut self, resolver: F) -> Self
105    where
106        F: Fn(&LoginMethodContext) -> Option<String> + Send + Sync + 'static,
107    {
108        self.resolver = Some(Arc::new(resolver));
109        self
110    }
111
112    #[must_use]
113    pub fn store_in_database(mut self, store_in_database: bool) -> Self {
114        self.store_in_database = Some(store_in_database);
115        self
116    }
117
118    #[must_use]
119    pub fn database_field_name(mut self, field_name: impl Into<String>) -> Self {
120        self.database_field_name = Some(field_name.into());
121        self
122    }
123
124    #[must_use]
125    pub fn build(self) -> LastLoginMethodOptions {
126        let defaults = LastLoginMethodOptions::default();
127        LastLoginMethodOptions {
128            cookie_name: self.cookie_name.or(defaults.cookie_name),
129            max_age: self.max_age.or(defaults.max_age),
130            resolver: self.resolver.or(defaults.resolver),
131            store_in_database: self.store_in_database.unwrap_or(defaults.store_in_database),
132            database_field_name: self.database_field_name.or(defaults.database_field_name),
133        }
134    }
135}