Skip to main content

embacle_server/
auth.rs

1// ABOUTME: Optional bearer token authentication middleware for the REST API
2// ABOUTME: Enforces EMBACLE_API_KEY when set, allows unauthenticated access otherwise
3//
4// SPDX-License-Identifier: Apache-2.0
5// Copyright (c) 2026 dravr.ai
6
7use axum::extract::Request;
8use axum::http::StatusCode;
9use axum::middleware::Next;
10use axum::response::{IntoResponse, Response};
11use axum::Json;
12
13use crate::openai_types::ErrorResponse;
14
15/// Environment variable name for the API key
16const API_KEY_ENV: &str = "EMBACLE_API_KEY";
17
18/// Middleware that validates the bearer token against `EMBACLE_API_KEY`
19///
20/// The env var is read on every request to allow runtime key rotation
21/// without restarting the server. If the variable is not set, all requests
22/// are allowed through (localhost development mode). If set, requests must
23/// include a matching `Authorization: Bearer <key>` header.
24pub async fn require_auth(request: Request, next: Next) -> Response {
25    let expected_key = match std::env::var(API_KEY_ENV) {
26        Ok(key) if !key.is_empty() => key,
27        _ => return next.run(request).await,
28    };
29
30    let auth_header = request
31        .headers()
32        .get("authorization")
33        .and_then(|v| v.to_str().ok());
34
35    match auth_header {
36        Some(header) if header.starts_with("Bearer ") => {
37            let token = &header["Bearer ".len()..];
38            if token == expected_key {
39                next.run(request).await
40            } else {
41                auth_error("Invalid API key")
42            }
43        }
44        Some(_) => auth_error("Authorization header must use Bearer scheme"),
45        None => auth_error("Missing Authorization header"),
46    }
47}
48
49/// Build a 401 error response
50fn auth_error(message: &str) -> Response {
51    let body = ErrorResponse::new("authentication_error", message);
52    (StatusCode::UNAUTHORIZED, Json(body)).into_response()
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn api_key_env_is_correct() {
61        assert_eq!(API_KEY_ENV, "EMBACLE_API_KEY");
62    }
63}