Skip to main content

modkit_auth/
config.rs

1use crate::validation::ValidationConfig;
2use serde::{Deserialize, Serialize};
3
4/// Main authentication configuration
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct AuthConfig {
7    /// Leeway in seconds for time-based validations (exp, nbf)
8    #[serde(default = "default_leeway")]
9    pub leeway_seconds: i64,
10
11    /// Allowed issuers (if empty, any issuer is accepted)
12    #[serde(default)]
13    pub issuers: Vec<String>,
14
15    /// Allowed audiences (if empty, any audience is accepted)
16    #[serde(default)]
17    pub audiences: Vec<String>,
18
19    /// JWKS configuration
20    #[serde(default)]
21    pub jwks: Option<JwksConfig>,
22}
23
24fn default_leeway() -> i64 {
25    60
26}
27
28impl Default for AuthConfig {
29    fn default() -> Self {
30        Self {
31            leeway_seconds: 60,
32            issuers: Vec::new(),
33            audiences: Vec::new(),
34            jwks: None,
35        }
36    }
37}
38
39impl From<&AuthConfig> for ValidationConfig {
40    fn from(config: &AuthConfig) -> Self {
41        Self {
42            allowed_issuers: config.issuers.clone(),
43            allowed_audiences: config.audiences.clone(),
44            leeway_seconds: config.leeway_seconds,
45        }
46    }
47}
48
49/// JWKS endpoint configuration
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct JwksConfig {
52    /// JWKS endpoint URL
53    pub uri: String,
54
55    /// Refresh interval in seconds (default: 300 = 5 minutes)
56    #[serde(default = "default_refresh_interval")]
57    pub refresh_interval_seconds: u64,
58
59    /// Maximum backoff in seconds (default: 3600 = 1 hour)
60    #[serde(default = "default_max_backoff")]
61    pub max_backoff_seconds: u64,
62}
63
64fn default_refresh_interval() -> u64 {
65    300
66}
67
68fn default_max_backoff() -> u64 {
69    3600
70}
71
72#[cfg(test)]
73#[cfg_attr(coverage_nightly, coverage(off))]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_default_config() {
79        let config = AuthConfig::default();
80        assert_eq!(config.leeway_seconds, 60);
81        assert!(config.issuers.is_empty());
82        assert!(config.audiences.is_empty());
83        assert!(config.jwks.is_none());
84    }
85
86    #[test]
87    fn test_auth_config_serialization() {
88        let config = AuthConfig {
89            leeway_seconds: 120,
90            issuers: vec!["https://auth.example.com".to_owned()],
91            audiences: vec!["api".to_owned()],
92            jwks: Some(JwksConfig {
93                uri: "https://auth.example.com/.well-known/jwks.json".to_owned(),
94                refresh_interval_seconds: 300,
95                max_backoff_seconds: 3600,
96            }),
97        };
98
99        let json = serde_json::to_string_pretty(&config).unwrap();
100        let deserialized: AuthConfig = serde_json::from_str(&json).unwrap();
101        assert_eq!(deserialized.leeway_seconds, 120);
102        assert_eq!(deserialized.issuers, vec!["https://auth.example.com"]);
103        assert_eq!(deserialized.audiences, vec!["api"]);
104        let jwks = deserialized.jwks.expect("jwks should be present");
105        assert_eq!(jwks.uri, "https://auth.example.com/.well-known/jwks.json");
106        assert_eq!(jwks.refresh_interval_seconds, 300);
107        assert_eq!(jwks.max_backoff_seconds, 3600);
108    }
109
110    #[test]
111    fn test_jwks_config_serialization() {
112        let config = JwksConfig {
113            uri: "https://auth.example.com/.well-known/jwks.json".to_owned(),
114            refresh_interval_seconds: 300,
115            max_backoff_seconds: 3600,
116        };
117
118        let json = serde_json::to_string_pretty(&config).unwrap();
119        let deserialized: JwksConfig = serde_json::from_str(&json).unwrap();
120        assert_eq!(deserialized.uri, config.uri);
121        assert_eq!(
122            deserialized.refresh_interval_seconds,
123            config.refresh_interval_seconds
124        );
125        assert_eq!(deserialized.max_backoff_seconds, config.max_backoff_seconds);
126    }
127
128    #[test]
129    fn test_auth_config_to_validation_config() {
130        let auth_config = AuthConfig {
131            leeway_seconds: 30,
132            issuers: vec!["https://auth.example.com".to_owned()],
133            audiences: vec!["api".to_owned()],
134            jwks: None,
135        };
136        let validation_config = ValidationConfig::from(&auth_config);
137        assert_eq!(validation_config.allowed_issuers, auth_config.issuers);
138        assert_eq!(validation_config.allowed_audiences, auth_config.audiences);
139        assert_eq!(validation_config.leeway_seconds, auth_config.leeway_seconds);
140    }
141
142    #[test]
143    fn test_jwks_config_defaults() {
144        let json = r#"{"uri": "https://example.com/jwks"}"#;
145        let config: JwksConfig = serde_json::from_str(json).unwrap();
146        assert_eq!(config.refresh_interval_seconds, 300);
147        assert_eq!(config.max_backoff_seconds, 3600);
148    }
149}