1pub use mockforge_core::config::{
12 ApiKeyConfig, AuthConfig, BasicAuthConfig, JwtConfig, OAuth2Config,
13};
14
15pub mod admin_auth;
17pub mod middleware;
18pub mod state;
19pub mod types;
20
21pub mod authenticator;
22pub mod jwks_converter;
23pub mod oauth2;
24pub mod oidc;
25pub mod risk_engine;
26pub mod token_lifecycle;
27
28pub use admin_auth::check_admin_auth;
30pub use authenticator::{authenticate_jwt, authenticate_request};
31pub use middleware::auth_middleware;
32pub use oauth2::create_oauth2_client;
33pub use state::AuthState;
34pub use types::{AuthClaims, AuthResult};
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39 use authenticator::{authenticate_api_key, authenticate_basic};
40 use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
41 use serde_json::json;
42 use std::collections::HashMap;
43 use std::sync::Arc;
44 use tokio::sync::RwLock;
45
46 #[test]
47 fn test_authenticate_basic_success() {
48 let mut credentials = HashMap::new();
49 credentials.insert("admin".to_string(), "password123".to_string());
50
51 let config = AuthConfig {
52 basic_auth: Some(BasicAuthConfig { credentials }),
53 ..Default::default()
54 };
55
56 let state = AuthState {
57 config,
58 spec: None,
59 oauth2_client: None,
60 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
61 };
62
63 let auth_header = "Basic YWRtaW46cGFzc3dvcmQxMjM="; let result = authenticate_basic(&state, auth_header);
65
66 match result {
67 Some(AuthResult::Success(claims)) => {
68 assert_eq!(claims.username, Some("admin".to_string()));
69 }
70 _ => panic!("Expected successful authentication"),
71 }
72 }
73
74 #[test]
75 fn test_authenticate_basic_invalid_credentials() {
76 let mut credentials = HashMap::new();
77 credentials.insert("admin".to_string(), "password123".to_string());
78
79 let config = AuthConfig {
80 basic_auth: Some(BasicAuthConfig { credentials }),
81 ..Default::default()
82 };
83
84 let state = AuthState {
85 config,
86 spec: None,
87 oauth2_client: None,
88 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
89 };
90
91 let auth_header = "Basic d3Jvbmd1c2VyOndyb25ncGFzcw=="; let result = authenticate_basic(&state, auth_header);
93
94 match result {
95 Some(AuthResult::Failure(_)) => {} _ => panic!("Expected authentication failure"),
97 }
98 }
99
100 #[test]
101 fn test_authenticate_basic_invalid_format() {
102 let config = AuthConfig {
103 basic_auth: Some(BasicAuthConfig {
104 credentials: HashMap::new(),
105 }),
106 ..Default::default()
107 };
108
109 let state = AuthState {
110 config,
111 spec: None,
112 oauth2_client: None,
113 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
114 };
115
116 let auth_header = "Basic invalidbase64";
117 let result = authenticate_basic(&state, auth_header);
118
119 match result {
120 Some(AuthResult::Failure(_)) => {} _ => panic!("Expected authentication failure"),
122 }
123 }
124
125 #[test]
126 fn test_authenticate_api_key_success() {
127 let config = AuthConfig {
128 api_key: Some(ApiKeyConfig {
129 header_name: "X-API-Key".to_string(),
130 query_name: None,
131 keys: vec!["valid-key-123".to_string()],
132 }),
133 ..Default::default()
134 };
135
136 let state = AuthState {
137 config,
138 spec: None,
139 oauth2_client: None,
140 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
141 };
142
143 let result = authenticate_api_key(&state, "valid-key-123");
144
145 match result {
146 Some(AuthResult::Success(claims)) => {
147 assert_eq!(
148 claims.custom.get("api_key"),
149 Some(&serde_json::Value::String("valid-key-123".to_string()))
150 );
151 }
152 _ => panic!("Expected successful authentication"),
153 }
154 }
155
156 #[test]
157 fn test_authenticate_api_key_invalid() {
158 let config = AuthConfig {
159 api_key: Some(ApiKeyConfig {
160 header_name: "X-API-Key".to_string(),
161 query_name: None,
162 keys: vec!["valid-key-123".to_string()],
163 }),
164 ..Default::default()
165 };
166
167 let state = AuthState {
168 config,
169 spec: None,
170 oauth2_client: None,
171 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
172 };
173
174 let result = authenticate_api_key(&state, "invalid-key");
175
176 match result {
177 Some(AuthResult::Failure(_)) => {} _ => panic!("Expected authentication failure"),
179 }
180 }
181
182 #[tokio::test]
183 async fn test_authenticate_jwt_hs256_success() {
184 let secret = "my-secret-key";
185 let config = AuthConfig {
186 jwt: Some(JwtConfig {
187 secret: Some(secret.to_string()),
188 rsa_public_key: None,
189 ecdsa_public_key: None,
190 issuer: Some("test-issuer".to_string()),
191 audience: Some("test-audience".to_string()),
192 algorithms: vec!["HS256".to_string()],
193 }),
194 ..Default::default()
195 };
196
197 let state = AuthState {
198 config,
199 spec: None,
200 oauth2_client: None,
201 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
202 };
203
204 let header = Header::new(Algorithm::HS256);
206 let claims = json!({
207 "sub": "user123",
208 "iss": "test-issuer",
209 "aud": "test-audience",
210 "exp": 2000000000, "iat": 1000000000,
212 "username": "testuser"
213 });
214
215 let token = encode(&header, &claims, &EncodingKey::from_secret(secret.as_bytes()))
216 .expect("Failed to create test JWT");
217
218 let auth_header = format!("Bearer {}", token);
219 let result = authenticate_jwt(&state, &auth_header).await;
220
221 match result {
222 Some(AuthResult::Success(claims)) => {
223 assert_eq!(claims.sub, Some("user123".to_string()));
224 assert_eq!(claims.iss, Some("test-issuer".to_string()));
225 assert_eq!(claims.aud, Some("test-audience".to_string()));
226 assert_eq!(claims.username, Some("testuser".to_string()));
227 }
228 _ => panic!("Expected successful authentication: {:?}", result),
229 }
230 }
231
232 #[tokio::test]
233 async fn test_authenticate_jwt_expired() {
234 let secret = "my-secret-key";
235 let config = AuthConfig {
236 jwt: Some(JwtConfig {
237 secret: Some(secret.to_string()),
238 rsa_public_key: None,
239 ecdsa_public_key: None,
240 issuer: None,
241 audience: None,
242 algorithms: vec!["HS256".to_string()],
243 }),
244 ..Default::default()
245 };
246
247 let state = AuthState {
248 config,
249 spec: None,
250 oauth2_client: None,
251 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
252 };
253
254 let header = Header::new(Algorithm::HS256);
256 let claims = json!({
257 "sub": "user123",
258 "exp": 1000000000, "iat": 900000000
260 });
261
262 let token = encode(&header, &claims, &EncodingKey::from_secret(secret.as_bytes()))
263 .expect("Failed to create test JWT");
264
265 let auth_header = format!("Bearer {}", token);
266 let result = authenticate_jwt(&state, &auth_header).await;
267
268 match result {
269 Some(AuthResult::Failure(_)) => {} _ => panic!("Expected authentication failure for expired token"),
271 }
272 }
273
274 #[tokio::test]
275 async fn test_authenticate_jwt_invalid_signature() {
276 let config = AuthConfig {
277 jwt: Some(JwtConfig {
278 secret: Some("correct-secret".to_string()),
279 rsa_public_key: None,
280 ecdsa_public_key: None,
281 issuer: None,
282 audience: None,
283 algorithms: vec!["HS256".to_string()],
284 }),
285 ..Default::default()
286 };
287
288 let state = AuthState {
289 config,
290 spec: None,
291 oauth2_client: None,
292 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
293 };
294
295 let header = Header::new(Algorithm::HS256);
297 let claims = json!({
298 "sub": "user123",
299 "exp": 2000000000
300 });
301
302 let token = encode(&header, &claims, &EncodingKey::from_secret("wrong-secret".as_bytes()))
303 .expect("Failed to create test JWT");
304
305 let auth_header = format!("Bearer {}", token);
306 let result = authenticate_jwt(&state, &auth_header).await;
307
308 match result {
309 Some(AuthResult::Failure(_)) => {} _ => panic!("Expected authentication failure for invalid signature"),
311 }
312 }
313
314 #[tokio::test]
315 async fn test_authenticate_request_no_auth_when_optional() {
316 let config = AuthConfig {
317 require_auth: false,
318 ..Default::default()
319 };
320
321 let state = AuthState {
322 config,
323 spec: None,
324 oauth2_client: None,
325 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
326 };
327
328 let result = authenticate_request(&state, &None, &None, &None).await;
329
330 match result {
331 AuthResult::None => {} _ => panic!("Expected no authentication required"),
333 }
334 }
335
336 #[tokio::test]
337 async fn test_authenticate_request_no_auth_when_required() {
338 let config = AuthConfig {
339 require_auth: true,
340 ..Default::default()
341 };
342
343 let state = AuthState {
344 config,
345 spec: None,
346 oauth2_client: None,
347 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
348 };
349
350 let result = authenticate_request(&state, &None, &None, &None).await;
351
352 match result {
353 AuthResult::None => {} _ => panic!("Expected no authentication provided"),
355 }
356 }
357
358 #[tokio::test]
359 async fn test_authenticate_request_with_valid_api_key() {
360 let config = AuthConfig {
361 api_key: Some(ApiKeyConfig {
362 header_name: "X-API-Key".to_string(),
363 query_name: None,
364 keys: vec!["valid-key".to_string()],
365 }),
366 ..Default::default()
367 };
368
369 let state = AuthState {
370 config,
371 spec: None,
372 oauth2_client: None,
373 introspection_cache: Arc::new(RwLock::new(HashMap::new())),
374 };
375
376 let api_key_header = Some("valid-key".to_string());
377 let result = authenticate_request(&state, &None, &api_key_header, &None).await;
378
379 match result {
380 AuthResult::Success(_) => {} _ => panic!("Expected successful authentication with API key"),
382 }
383 }
384
385 #[test]
386 fn test_create_oauth2_client_success() {
387 let config = OAuth2Config {
388 client_id: "test-client".to_string(),
389 client_secret: "test-secret".to_string(),
390 introspection_url: "https://example.com/introspect".to_string(),
391 auth_url: Some("https://example.com/auth".to_string()),
392 token_url: Some("https://example.com/token".to_string()),
393 token_type_hint: Some("access_token".to_string()),
394 };
395
396 let result = create_oauth2_client(&config);
397 assert!(result.is_ok());
398 }
399
400 #[test]
401 fn test_create_oauth2_client_invalid_url() {
402 let config = OAuth2Config {
403 client_id: "test-client".to_string(),
404 client_secret: "test-secret".to_string(),
405 introspection_url: "https://example.com/introspect".to_string(),
406 auth_url: Some("not-a-valid-url".to_string()),
407 token_url: None,
408 token_type_hint: None,
409 };
410
411 let result = create_oauth2_client(&config);
412 assert!(result.is_err());
413 }
414
415 #[test]
416 fn test_auth_config_default() {
417 let config = AuthConfig::default();
418 assert!(!config.require_auth);
419 assert!(config.jwt.is_none());
420 assert!(config.oauth2.is_none());
421 assert!(config.basic_auth.is_none());
422 assert!(config.api_key.is_some()); }
424
425 #[test]
426 fn test_auth_claims_new() {
427 let claims = AuthClaims::new();
428 assert!(claims.sub.is_none());
429 assert!(claims.iss.is_none());
430 assert!(claims.aud.is_none());
431 assert!(claims.exp.is_none());
432 assert!(claims.iat.is_none());
433 assert!(claims.username.is_none());
434 assert!(claims.roles.is_empty());
435 assert!(claims.custom.is_empty());
436 }
437}