spec_ai_api/api/
middleware.rs

1/// API authentication and middleware
2use axum::{
3    extract::Request,
4    http::{HeaderMap, StatusCode},
5    middleware::Next,
6    response::Response,
7};
8
9/// API key authentication middleware
10pub 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    /// Check if API key authentication is enabled
20    pub fn is_enabled(&self) -> bool {
21        self.api_key.is_some()
22    }
23
24    /// Validate an API key
25    pub fn validate(&self, key: &str) -> bool {
26        match &self.api_key {
27            Some(expected) => expected == key,
28            None => true, // No auth required if not configured
29        }
30    }
31}
32
33/// Axum middleware function for API key authentication
34pub async fn auth_middleware(
35    headers: HeaderMap,
36    request: Request,
37    next: Next,
38) -> Result<Response, StatusCode> {
39    // Get API key from state (would be injected via layer)
40    // For now, we'll extract from headers
41
42    if let Some(auth_header) = headers.get("Authorization") {
43        if let Ok(auth_str) = auth_header.to_str() {
44            // Support both "Bearer <key>" and direct key
45            let key = if auth_str.starts_with("Bearer ") {
46                &auth_str[7..]
47            } else {
48                auth_str
49            };
50
51            // In production, validate against configured key
52            // For now, accept any non-empty key
53            if !key.is_empty() {
54                return Ok(next.run(request).await);
55            }
56        }
57    }
58
59    // If no API key required (development mode), allow through
60    // In production, this would reject
61    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}