Skip to main content

rustauth_plugins/two_factor/
options.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use rustauth_core::api::ApiRequest;
6use rustauth_core::db::User;
7use rustauth_core::error::RustAuthError;
8use time::Duration;
9
10pub type SendOtpFuture = Pin<Box<dyn Future<Output = Result<(), RustAuthError>> + Send>>;
11pub type SendOtp = Arc<dyn Fn(TwoFactorOtpMessage) -> SendOtpFuture + Send + Sync>;
12
13#[derive(Clone)]
14pub struct TwoFactorOtpMessage {
15    pub user: User,
16    pub otp: String,
17    pub request: ApiRequest,
18}
19
20#[derive(Clone)]
21pub struct TwoFactorOptions {
22    pub issuer: Option<String>,
23    /// Physical database table name for two-factor secrets (`two_factors` by default).
24    pub two_factor_table: String,
25    pub totp: TotpOptions,
26    pub otp: OtpOptions,
27    pub backup_codes: BackupCodeOptions,
28    pub skip_verification_on_enable: bool,
29    pub allow_passwordless: bool,
30    pub two_factor_cookie_max_age: Duration,
31    pub trust_device_max_age: Duration,
32}
33
34impl Default for TwoFactorOptions {
35    fn default() -> Self {
36        Self {
37            issuer: None,
38            two_factor_table: "two_factors".to_owned(),
39            totp: TotpOptions::default(),
40            otp: OtpOptions::default(),
41            backup_codes: BackupCodeOptions::default(),
42            skip_verification_on_enable: false,
43            allow_passwordless: false,
44            two_factor_cookie_max_age: Duration::minutes(10),
45            trust_device_max_age: Duration::days(30),
46        }
47    }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct TotpOptions {
52    pub digits: u32,
53    pub period: Duration,
54    pub disabled: bool,
55}
56
57impl Default for TotpOptions {
58    fn default() -> Self {
59        Self {
60            digits: 6,
61            period: Duration::seconds(30),
62            disabled: false,
63        }
64    }
65}
66
67#[derive(Clone)]
68pub struct OtpOptions {
69    pub period: Duration,
70    pub digits: usize,
71    pub allowed_attempts: u32,
72    pub storage: OtpStorage,
73    pub send_otp: Option<SendOtp>,
74}
75
76impl Default for OtpOptions {
77    fn default() -> Self {
78        Self {
79            period: Duration::minutes(3),
80            digits: 6,
81            allowed_attempts: 5,
82            storage: OtpStorage::Plain,
83            send_otp: None,
84        }
85    }
86}
87
88pub use super::otp_storage::{OtpDecryptFn, OtpEncryptFn, OtpHashFn, OtpStorage};
89
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct BackupCodeOptions {
92    pub amount: usize,
93    pub length: usize,
94    pub storage: BackupCodeStorage,
95}
96
97impl Default for BackupCodeOptions {
98    fn default() -> Self {
99        Self {
100            amount: 10,
101            length: 10,
102            storage: BackupCodeStorage::Encrypted,
103        }
104    }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum BackupCodeStorage {
109    Plain,
110    Encrypted,
111}
112
113#[derive(Clone, Default)]
114pub struct TwoFactorOptionsBuilder {
115    issuer: Option<Option<String>>,
116    two_factor_table: Option<String>,
117    totp: Option<TotpOptions>,
118    otp: Option<OtpOptions>,
119    backup_codes: Option<BackupCodeOptions>,
120    skip_verification_on_enable: Option<bool>,
121    allow_passwordless: Option<bool>,
122    two_factor_cookie_max_age: Option<Duration>,
123    trust_device_max_age: Option<Duration>,
124}
125
126impl TwoFactorOptionsBuilder {
127    pub fn build(self) -> TwoFactorOptions {
128        let defaults = TwoFactorOptions::default();
129        TwoFactorOptions {
130            issuer: self.issuer.unwrap_or(defaults.issuer),
131            two_factor_table: self.two_factor_table.unwrap_or(defaults.two_factor_table),
132            totp: self.totp.unwrap_or(defaults.totp),
133            otp: self.otp.unwrap_or(defaults.otp),
134            backup_codes: self.backup_codes.unwrap_or(defaults.backup_codes),
135            skip_verification_on_enable: self
136                .skip_verification_on_enable
137                .unwrap_or(defaults.skip_verification_on_enable),
138            allow_passwordless: self
139                .allow_passwordless
140                .unwrap_or(defaults.allow_passwordless),
141            two_factor_cookie_max_age: self
142                .two_factor_cookie_max_age
143                .unwrap_or(defaults.two_factor_cookie_max_age),
144            trust_device_max_age: self
145                .trust_device_max_age
146                .unwrap_or(defaults.trust_device_max_age),
147        }
148    }
149}
150
151impl TwoFactorOptions {
152    #[must_use]
153    pub fn builder() -> TwoFactorOptionsBuilder {
154        TwoFactorOptionsBuilder::default()
155    }
156}