Skip to main content

webgates_sessions/
config.rs

1//! Typed session configuration values.
2//!
3//! This module defines framework-agnostic configuration for session issuance,
4//! renewal, refresh-token rotation, and lease coordination.
5
6use std::time::Duration;
7
8use crate::errors::ConfigError;
9use crate::lease::LeaseTtl;
10
11/// Result returned by session configuration validation.
12pub type ConfigResult<T> = std::result::Result<T, ConfigError>;
13
14/// Configuration for session issuance and renewal behavior.
15///
16/// This type intentionally stays free of HTTP-specific concerns such as cookie
17/// names, headers, or response mutation. Adapter crates should translate these
18/// values into transport-specific behavior at the system edge.
19///
20/// # Examples
21///
22/// ```
23/// use std::time::Duration;
24/// use webgates_sessions::config::SessionConfig;
25/// use webgates_sessions::lease::LeaseTtl;
26///
27/// // Use defaults for a typical production setup.
28/// let config = SessionConfig::default();
29/// assert!(config.is_valid());
30///
31/// // Or supply explicit values.
32/// let config = SessionConfig::new(
33///     Duration::from_secs(15 * 60),     // auth token TTL: 15 min
34///     Duration::from_secs(30 * 24 * 3600), // refresh token TTL: 30 days
35///     Duration::from_secs(2 * 60),      // proactive renewal window: 2 min
36///     LeaseTtl::new(Duration::from_secs(30)),
37///     Duration::from_secs(5),           // clock-skew tolerance: 5 s
38/// )
39/// .validate()
40/// .unwrap();
41///
42/// assert!(config.is_valid());
43/// ```
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct SessionConfig {
46    /// Lifetime of the short-lived authentication token.
47    pub auth_token_ttl: Duration,
48    /// Lifetime of the long-lived refresh token.
49    pub refresh_token_ttl: Duration,
50    /// Window before auth-token expiry during which proactive renewal is
51    /// allowed.
52    pub proactive_renewal_window: Duration,
53    /// Typed duration for which a renewal lease remains valid once acquired.
54    pub renewal_lease_ttl: LeaseTtl,
55    /// Maximum amount of clock skew tolerated by renewal decision logic.
56    pub clock_skew_tolerance: Duration,
57}
58
59impl SessionConfig {
60    /// Creates a configuration with explicit values.
61    #[must_use]
62    pub const fn new(
63        auth_token_ttl: Duration,
64        refresh_token_ttl: Duration,
65        proactive_renewal_window: Duration,
66        renewal_lease_ttl: LeaseTtl,
67        clock_skew_tolerance: Duration,
68    ) -> Self {
69        Self {
70            auth_token_ttl,
71            refresh_token_ttl,
72            proactive_renewal_window,
73            renewal_lease_ttl,
74            clock_skew_tolerance,
75        }
76    }
77
78    /// Returns the configured lease TTL as a raw duration.
79    #[must_use]
80    pub fn renewal_lease_duration(&self) -> Duration {
81        self.renewal_lease_ttl.duration()
82    }
83
84    /// Validates the configuration and returns the typed value when it is
85    /// internally consistent.
86    ///
87    /// # Errors
88    ///
89    /// Returns a configuration error when one of the required durations is
90    /// zero or when the proactive renewal window exceeds the auth-token
91    /// lifetime.
92    pub fn validate(self) -> ConfigResult<Self> {
93        if self.auth_token_ttl.is_zero()
94            || self.refresh_token_ttl.is_zero()
95            || self.renewal_lease_duration().is_zero()
96        {
97            return Err(ConfigError::Invalid);
98        }
99
100        if self.proactive_renewal_window > self.auth_token_ttl {
101            return Err(ConfigError::Invalid);
102        }
103
104        Ok(self)
105    }
106
107    /// Returns `true` when the configuration is internally consistent.
108    #[must_use]
109    pub fn is_valid(&self) -> bool {
110        self.clone().validate().is_ok()
111    }
112}
113
114impl Default for SessionConfig {
115    fn default() -> Self {
116        Self {
117            auth_token_ttl: Duration::from_secs(15 * 60),
118            refresh_token_ttl: Duration::from_secs(30 * 24 * 60 * 60),
119            proactive_renewal_window: Duration::from_secs(2 * 60),
120            renewal_lease_ttl: LeaseTtl::default(),
121            clock_skew_tolerance: Duration::from_secs(5),
122        }
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::SessionConfig;
129    use crate::lease::LeaseTtl;
130    use std::time::Duration;
131
132    #[test]
133    fn default_config_is_valid() {
134        assert!(SessionConfig::default().is_valid());
135    }
136
137    #[test]
138    fn config_exposes_typed_lease_duration() {
139        let config = SessionConfig::default();
140
141        assert_eq!(
142            config.renewal_lease_duration(),
143            LeaseTtl::default().duration()
144        );
145    }
146
147    #[test]
148    fn config_is_invalid_when_proactive_window_exceeds_auth_ttl() {
149        let config = SessionConfig::new(
150            Duration::from_secs(60),
151            Duration::from_secs(3600),
152            Duration::from_secs(61),
153            LeaseTtl::new(Duration::from_secs(30)),
154            Duration::from_secs(5),
155        );
156
157        assert!(!config.is_valid());
158    }
159
160    #[test]
161    fn config_is_invalid_when_required_durations_are_zero() {
162        let config = SessionConfig::new(
163            Duration::ZERO,
164            Duration::from_secs(3600),
165            Duration::ZERO,
166            LeaseTtl::new(Duration::from_secs(30)),
167            Duration::from_secs(5),
168        );
169
170        assert!(!config.is_valid());
171    }
172
173    #[test]
174    fn config_is_invalid_when_lease_ttl_is_zero() {
175        let config = SessionConfig::new(
176            Duration::from_secs(60),
177            Duration::from_secs(3600),
178            Duration::from_secs(30),
179            LeaseTtl::new(Duration::ZERO),
180            Duration::from_secs(5),
181        );
182
183        assert!(!config.is_valid());
184    }
185}