1use std::time::Duration;
2
3const DEFAULT_JWT_EXPIRATION_SECS: u64 = 300;
5
6const DEFAULT_SIGNING_SERVICE_URL: &str = "http://localhost:8000/signature";
8
9const DEFAULT_HTTP_TIMEOUT_SECS: u64 = 3;
11
12const DEFAULT_MAX_DISCOVERY_RETRIES: u32 = 10;
14
15const DEFAULT_DISCOVERY_BACKOFF_SECS: u64 = 2;
17
18#[derive(Debug, Clone)]
20pub struct OnesOidcConfig {
21 pub http_timeout: Duration,
23 pub user_agent: Option<String>,
25 pub jwt_expiration_secs: u64,
27 pub signing_service_url: String,
29 pub validate_jwt_audience: bool,
31 pub max_discovery_retries: u32,
33 pub discovery_backoff: Duration,
35}
36
37impl Default for OnesOidcConfig {
38 fn default() -> Self {
39 Self {
40 http_timeout: Duration::from_secs(DEFAULT_HTTP_TIMEOUT_SECS),
41 user_agent: Some(format!("ones-oidc/{}", env!("CARGO_PKG_VERSION"))),
42 jwt_expiration_secs: DEFAULT_JWT_EXPIRATION_SECS,
43 signing_service_url: DEFAULT_SIGNING_SERVICE_URL.to_string(),
44 validate_jwt_audience: false,
45 max_discovery_retries: DEFAULT_MAX_DISCOVERY_RETRIES,
46 discovery_backoff: Duration::from_secs(DEFAULT_DISCOVERY_BACKOFF_SECS),
47 }
48 }
49}
50
51impl OnesOidcConfig {
52 pub fn with_timeout(timeout: Duration) -> Self {
54 Self {
55 http_timeout: timeout,
56 ..Default::default()
57 }
58 }
59
60 pub fn with_signing_service_url(url: String) -> Self {
62 Self {
63 signing_service_url: url,
64 ..Default::default()
65 }
66 }
67
68 pub fn with_jwt_audience_validation(enabled: bool) -> Self {
70 Self {
71 validate_jwt_audience: enabled,
72 ..Default::default()
73 }
74 }
75
76 pub fn with_jwt_expiration(expiration_secs: u64) -> Self {
78 Self {
79 jwt_expiration_secs: expiration_secs,
80 ..Default::default()
81 }
82 }
83
84 pub fn timeout(mut self, timeout: Duration) -> Self {
86 self.http_timeout = timeout;
87 self
88 }
89
90 pub fn signing_service_url(mut self, url: String) -> Self {
92 self.signing_service_url = url;
93 self
94 }
95
96 pub fn jwt_audience_validation(mut self, enabled: bool) -> Self {
98 self.validate_jwt_audience = enabled;
99 self
100 }
101
102 pub fn jwt_expiration(mut self, expiration_secs: u64) -> Self {
104 self.jwt_expiration_secs = expiration_secs;
105 self
106 }
107
108 pub fn max_discovery_retries(mut self, retries: u32) -> Self {
110 self.max_discovery_retries = retries;
111 self
112 }
113
114 pub fn discovery_backoff(mut self, backoff: Duration) -> Self {
116 self.discovery_backoff = backoff;
117 self
118 }
119
120 pub fn from_env() -> Self {
122 let mut config = Self::default();
123
124 if let Ok(timeout_str) = std::env::var("ONES_OIDC_TIMEOUT_SECS") {
126 if let Ok(timeout_secs) = timeout_str.parse::<u64>() {
127 config.http_timeout = Duration::from_secs(timeout_secs);
128 }
129 }
130
131 if let Ok(signing_url) = std::env::var("ONES_OIDC_SIGNING_SERVICE_URL") {
133 config.signing_service_url = signing_url;
134 }
135
136 if let Ok(validate_str) = std::env::var("ONES_OIDC_VALIDATE_JWT_AUDIENCE") {
138 config.validate_jwt_audience = validate_str.to_lowercase() == "true";
139 }
140
141 if let Ok(exp_str) = std::env::var("ONES_OIDC_JWT_EXPIRATION_SECS") {
143 if let Ok(exp_secs) = exp_str.parse::<u64>() {
144 config.jwt_expiration_secs = exp_secs;
145 }
146 }
147
148 if let Ok(retries_str) = std::env::var("ONES_OIDC_MAX_DISCOVERY_RETRIES") {
150 if let Ok(retries) = retries_str.parse::<u32>() {
151 config.max_discovery_retries = retries;
152 }
153 }
154
155 if let Ok(backoff_str) = std::env::var("ONES_OIDC_DISCOVERY_BACKOFF_SECS") {
157 if let Ok(backoff_secs) = backoff_str.parse::<u64>() {
158 config.discovery_backoff = Duration::from_secs(backoff_secs);
159 }
160 }
161
162 config
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn test_default_config() {
172 let config = OnesOidcConfig::default();
173 assert_eq!(config.jwt_expiration_secs, DEFAULT_JWT_EXPIRATION_SECS);
174 assert_eq!(config.signing_service_url, DEFAULT_SIGNING_SERVICE_URL);
175 assert!(!config.validate_jwt_audience);
176 assert_eq!(config.max_discovery_retries, DEFAULT_MAX_DISCOVERY_RETRIES);
177 assert_eq!(config.discovery_backoff, Duration::from_secs(DEFAULT_DISCOVERY_BACKOFF_SECS));
178 }
179
180 #[test]
181 fn test_builder_pattern() {
182 let config = OnesOidcConfig::default()
183 .timeout(Duration::from_secs(10))
184 .signing_service_url("https://production.example.com/signature".to_string())
185 .jwt_audience_validation(true)
186 .jwt_expiration(600)
187 .max_discovery_retries(5)
188 .discovery_backoff(Duration::from_secs(3));
189
190 assert_eq!(config.http_timeout, Duration::from_secs(10));
191 assert_eq!(config.signing_service_url, "https://production.example.com/signature");
192 assert!(config.validate_jwt_audience);
193 assert_eq!(config.jwt_expiration_secs, 600);
194 assert_eq!(config.max_discovery_retries, 5);
195 assert_eq!(config.discovery_backoff, Duration::from_secs(3));
196 }
197
198 #[test]
199 fn test_from_env() {
200 std::env::set_var("ONES_OIDC_TIMEOUT_SECS", "5");
202 std::env::set_var("ONES_OIDC_SIGNING_SERVICE_URL", "https://test.example.com/sign");
203 std::env::set_var("ONES_OIDC_VALIDATE_JWT_AUDIENCE", "true");
204 std::env::set_var("ONES_OIDC_JWT_EXPIRATION_SECS", "120");
205 std::env::set_var("ONES_OIDC_MAX_DISCOVERY_RETRIES", "3");
206 std::env::set_var("ONES_OIDC_DISCOVERY_BACKOFF_SECS", "5");
207
208 let config = OnesOidcConfig::from_env();
209
210 assert_eq!(config.http_timeout, Duration::from_secs(5));
211 assert_eq!(config.signing_service_url, "https://test.example.com/sign");
212 assert!(config.validate_jwt_audience);
213 assert_eq!(config.jwt_expiration_secs, 120);
214 assert_eq!(config.max_discovery_retries, 3);
215 assert_eq!(config.discovery_backoff, Duration::from_secs(5));
216
217 std::env::remove_var("ONES_OIDC_TIMEOUT_SECS");
219 std::env::remove_var("ONES_OIDC_SIGNING_SERVICE_URL");
220 std::env::remove_var("ONES_OIDC_VALIDATE_JWT_AUDIENCE");
221 std::env::remove_var("ONES_OIDC_JWT_EXPIRATION_SECS");
222 std::env::remove_var("ONES_OIDC_MAX_DISCOVERY_RETRIES");
223 std::env::remove_var("ONES_OIDC_DISCOVERY_BACKOFF_SECS");
224 }
225}