oxidite_auth/
api_key_middleware.rs

1use oxidite_core::{OxiditeRequest, Result as OxiditeResult, Error};
2use oxidite_db::Database;
3use std::sync::Arc;
4use crate::api_key::ApiKey;
5
6/// Middleware to authenticate requests using API keys
7pub struct ApiKeyMiddleware {
8    db: Arc<dyn Database>,
9}
10
11impl ApiKeyMiddleware {
12    pub fn new(db: Arc<dyn Database>) -> Self {
13        Self { db }
14    }
15    
16    /// Extract and verify API key from request
17    pub async fn authenticate(&self, req: &mut OxiditeRequest) -> OxiditeResult<i64> {
18        // Extract API key from Authorization header or query parameter
19        let key = self.extract_key(req)?;
20        
21        // Verify the key
22        let api_key = ApiKey::verify_key(&*self.db, &key).await
23            .map_err(|_| Error::InternalServerError("Database error".to_string()))?
24            .ok_or_else(|| Error::Unauthorized("Invalid or expired API key".to_string()))?;
25        
26        // Store user_id in request extensions
27        req.extensions_mut().insert(api_key.user_id);
28        
29        Ok(api_key.user_id)
30    }
31    
32    /// Extract API key from request headers or query string
33    fn extract_key(&self, req: &OxiditeRequest) -> OxiditeResult<String> {
34        // Try Authorization header first (Bearer token style)
35        if let Some(auth_header) = req.headers().get("authorization") {
36            if let Ok(auth_str) = auth_header.to_str() {
37                if let Some(key) = auth_str.strip_prefix("Bearer ") {
38                    return Ok(key.to_string());
39                }
40            }
41        }
42        
43        // Try X-API-Key header
44        if let Some(api_key_header) = req.headers().get("x-api-key") {
45            if let Ok(key) = api_key_header.to_str() {
46                return Ok(key.to_string());
47            }
48        }
49        
50        // Try query parameter
51        if let Some(query) = req.uri().query() {
52            for param in query.split('&') {
53                if let Some((k, v)) = param.split_once('=') {
54                    if k == "api_key" {
55                        return Ok(v.to_string());
56                    }
57                }
58            }
59        }
60        
61        Err(Error::Unauthorized("API key required".to_string()))
62    }
63}