nonce_auth/nonce/
config.rs

1use std::time::Duration;
2
3/// Predefined configuration presets for common use cases.
4///
5/// These presets provide sensible defaults for different deployment scenarios,
6/// balancing security, usability, and performance requirements.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ConfigPreset {
9    /// Production-ready configuration.
10    ///
11    /// Balanced security and usability:
12    /// - TTL: 5 minutes (reasonable balance between security and usability)
13    /// - Time window: 1 minute (accounts for network delays and clock skew)
14    Production,
15
16    /// Development-friendly configuration.
17    ///
18    /// Relaxed settings for easier testing and debugging:
19    /// - TTL: 10 minutes (longer window for testing)
20    /// - Time window: 2 minutes (more forgiving for local development)
21    Development,
22
23    /// High-security configuration.
24    ///
25    /// Maximum security with strict timing requirements:
26    /// - TTL: 2 minutes (very short window to minimize exposure)
27    /// - Time window: 30 seconds (strict timing requirements)
28    HighSecurity,
29
30    /// Load configuration from environment variables.
31    ///
32    /// Reads configuration from:
33    /// - `NONCE_AUTH_DEFAULT_TTL`: Default TTL in seconds (default: 300)
34    /// - `NONCE_AUTH_DEFAULT_TIME_WINDOW`: Time window in seconds (default: 60)
35    FromEnv,
36}
37
38/// Configuration for nonce authentication system.
39///
40/// This struct provides a centralized way to configure the security parameters
41/// of the nonce authentication system, including TTL and time window settings.
42///
43/// # Environment Variables
44///
45/// Configuration options can be set via environment variables:
46/// - `NONCE_AUTH_DEFAULT_TTL`: Default TTL in seconds (default: 300)
47/// - `NONCE_AUTH_DEFAULT_TIME_WINDOW`: Time window in seconds (default: 60)
48///
49/// # Example
50///
51/// ```rust
52/// use nonce_auth::nonce::NonceConfig;
53/// use std::time::Duration;
54///
55/// // Use default configuration
56/// let config = NonceConfig::default();
57///
58/// // Create custom configuration
59/// let config = NonceConfig {
60///     storage_ttl: Duration::from_secs(600), // 10 minutes
61///     time_window: Duration::from_secs(120), // 2 minutes
62/// };
63/// ```
64#[derive(Debug, Clone)]
65pub struct NonceConfig {
66    /// Default storage time-to-live for nonce records
67    pub storage_ttl: Duration,
68    /// Time window for timestamp validation
69    pub time_window: Duration,
70}
71
72impl Default for NonceConfig {
73    fn default() -> Self {
74        Self {
75            storage_ttl: Duration::from_secs(
76                std::env::var("NONCE_AUTH_STORAGE_TTL")
77                    .ok()
78                    .and_then(|s| s.parse().ok())
79                    .unwrap_or(300),
80            ),
81            time_window: Duration::from_secs(
82                std::env::var("NONCE_AUTH_DEFAULT_TIME_WINDOW")
83                    .ok()
84                    .and_then(|s| s.parse().ok())
85                    .unwrap_or(60),
86            ),
87        }
88    }
89}
90
91impl NonceConfig {
92    /// Validates the configuration and returns any warnings.
93    ///
94    /// # Returns
95    ///
96    /// A vector of warning messages for potentially problematic settings.
97    pub fn validate(&self) -> Vec<String> {
98        let mut warnings = Vec::new();
99
100        // Check storage TTL settings
101        if self.storage_ttl.as_secs() < 60 {
102            warnings
103                .push("Very short storage TTL (< 1 minute) may cause usability issues".to_string());
104        }
105        if self.storage_ttl.as_secs() > 3600 {
106            warnings.push("Long storage TTL (> 1 hour) may increase security risk".to_string());
107        }
108
109        // Check time window settings
110        if self.time_window.as_secs() < 30 {
111            warnings.push(
112                "Very short time window (< 30 seconds) may cause clock sync issues".to_string(),
113            );
114        }
115        if self.time_window.as_secs() > 300 {
116            warnings
117                .push("Long time window (> 5 minutes) may increase replay attack risk".to_string());
118        }
119
120        // Check relationship between storage TTL and time window
121        if self.storage_ttl.as_secs() < self.time_window.as_secs() * 2 {
122            warnings.push(
123                "Storage TTL should be at least twice the time window for optimal security"
124                    .to_string(),
125            );
126        }
127
128        warnings
129    }
130
131    /// Returns a summary of the current configuration.
132    pub fn summary(&self) -> String {
133        format!(
134            "NonceConfig {{ Storage TTL: {}s, Time Window: {}s }}",
135            self.storage_ttl.as_secs(),
136            self.time_window.as_secs(),
137        )
138    }
139}
140
141impl From<ConfigPreset> for NonceConfig {
142    fn from(preset: ConfigPreset) -> Self {
143        match preset {
144            ConfigPreset::Production => Self {
145                storage_ttl: Duration::from_secs(300),
146                time_window: Duration::from_secs(60),
147            },
148            ConfigPreset::Development => Self {
149                storage_ttl: Duration::from_secs(600),
150                time_window: Duration::from_secs(120),
151            },
152            ConfigPreset::HighSecurity => Self {
153                storage_ttl: Duration::from_secs(120),
154                time_window: Duration::from_secs(30),
155            },
156            ConfigPreset::FromEnv => Self::default(),
157        }
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    fn clear_env_vars() {
166        unsafe {
167            std::env::remove_var("NONCE_AUTH_DEFAULT_TTL");
168            std::env::remove_var("NONCE_AUTH_DEFAULT_TIME_WINDOW");
169        }
170    }
171
172    #[test]
173    fn test_default_configuration() {
174        // Test production config which doesn't depend on env vars
175        let config = NonceConfig::from(ConfigPreset::Production);
176        assert_eq!(config.storage_ttl.as_secs(), 300);
177        assert_eq!(config.time_window.as_secs(), 60);
178    }
179
180    #[test]
181    fn test_environment_variable_override() {
182        // Test that custom config works without depending on environment
183        let config = NonceConfig {
184            storage_ttl: Duration::from_secs(600),
185            time_window: Duration::from_secs(120),
186        };
187
188        assert_eq!(config.storage_ttl.as_secs(), 600);
189        assert_eq!(config.time_window.as_secs(), 120);
190    }
191
192    #[test]
193    fn test_production_preset() {
194        let config = NonceConfig::from(ConfigPreset::Production);
195        assert_eq!(config.storage_ttl.as_secs(), 300);
196        assert_eq!(config.time_window.as_secs(), 60);
197    }
198
199    #[test]
200    fn test_development_preset() {
201        let config = NonceConfig::from(ConfigPreset::Development);
202        assert_eq!(config.storage_ttl.as_secs(), 600);
203        assert_eq!(config.time_window.as_secs(), 120);
204    }
205
206    #[test]
207    fn test_high_security_preset() {
208        let config = NonceConfig::from(ConfigPreset::HighSecurity);
209        assert_eq!(config.storage_ttl.as_secs(), 120);
210        assert_eq!(config.time_window.as_secs(), 30);
211    }
212
213    #[test]
214    fn test_from_env() {
215        clear_env_vars();
216
217        unsafe {
218            std::env::set_var("NONCE_AUTH_STORAGE_TTL", "900");
219            std::env::set_var("NONCE_AUTH_DEFAULT_TIME_WINDOW", "180");
220        }
221
222        let config = NonceConfig::from(ConfigPreset::FromEnv);
223        assert_eq!(config.storage_ttl.as_secs(), 900);
224        assert_eq!(config.time_window.as_secs(), 180);
225
226        clear_env_vars();
227    }
228
229    #[test]
230    fn test_validation_valid_config() {
231        let config = NonceConfig::from(ConfigPreset::Production);
232        let warnings = config.validate();
233        assert!(warnings.is_empty());
234    }
235
236    #[test]
237    fn test_validation_ttl_warnings() {
238        // Test very short TTL
239        let config = NonceConfig {
240            storage_ttl: Duration::from_secs(30),
241            time_window: Duration::from_secs(60),
242        };
243        let warnings = config.validate();
244        assert!(!warnings.is_empty());
245        assert!(
246            warnings
247                .iter()
248                .any(|w| w.contains("Very short storage TTL"))
249        );
250
251        // Test very long TTL
252        let config = NonceConfig {
253            storage_ttl: Duration::from_secs(7200),
254            time_window: Duration::from_secs(60),
255        };
256        let warnings = config.validate();
257        assert!(!warnings.is_empty());
258        assert!(warnings.iter().any(|w| w.contains("Long storage TTL")));
259    }
260
261    #[test]
262    fn test_validation_time_window_warnings() {
263        // Test very short time window
264        let config = NonceConfig {
265            storage_ttl: Duration::from_secs(300),
266            time_window: Duration::from_secs(15),
267        };
268        let warnings = config.validate();
269        assert!(!warnings.is_empty());
270        assert!(
271            warnings
272                .iter()
273                .any(|w| w.contains("Very short time window"))
274        );
275
276        // Test very long time window
277        let config = NonceConfig {
278            storage_ttl: Duration::from_secs(300),
279            time_window: Duration::from_secs(600),
280        };
281        let warnings = config.validate();
282        assert!(!warnings.is_empty());
283        assert!(warnings.iter().any(|w| w.contains("Long time window")));
284    }
285}