spec_ai_api/api/
middleware.rs1use axum::{
3 extract::Request,
4 http::{HeaderMap, StatusCode},
5 middleware::Next,
6 response::Response,
7};
8
9pub struct ApiKeyAuth {
11 api_key: Option<String>,
12}
13
14impl ApiKeyAuth {
15 pub fn new(api_key: Option<String>) -> Self {
16 Self { api_key }
17 }
18
19 pub fn is_enabled(&self) -> bool {
21 self.api_key.is_some()
22 }
23
24 pub fn validate(&self, key: &str) -> bool {
26 match &self.api_key {
27 Some(expected) => expected == key,
28 None => true, }
30 }
31}
32
33pub async fn auth_middleware(
35 headers: HeaderMap,
36 request: Request,
37 next: Next,
38) -> Result<Response, StatusCode> {
39 if let Some(auth_header) = headers.get("Authorization") {
43 if let Ok(auth_str) = auth_header.to_str() {
44 let key = if auth_str.starts_with("Bearer ") {
46 &auth_str[7..]
47 } else {
48 auth_str
49 };
50
51 if !key.is_empty() {
54 return Ok(next.run(request).await);
55 }
56 }
57 }
58
59 Ok(next.run(request).await)
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn test_api_key_auth_disabled() {
70 let auth = ApiKeyAuth::new(None);
71 assert!(!auth.is_enabled());
72 assert!(auth.validate("any_key"));
73 }
74
75 #[test]
76 fn test_api_key_auth_enabled() {
77 let auth = ApiKeyAuth::new(Some("secret123".to_string()));
78 assert!(auth.is_enabled());
79 assert!(auth.validate("secret123"));
80 assert!(!auth.validate("wrong_key"));
81 }
82
83 #[test]
84 fn test_api_key_validation() {
85 let auth = ApiKeyAuth::new(Some("my-secret-key".to_string()));
86
87 assert!(auth.validate("my-secret-key"));
88 assert!(!auth.validate(""));
89 assert!(!auth.validate("wrong"));
90 }
91}