use crate::AppState;
use axum::{
Json,
extract::{Request, State},
http::StatusCode,
middleware::Next,
response::{IntoResponse, Response},
};
use crabllm_core::{ApiError, Provider, Storage};
#[derive(Clone, Debug)]
pub struct Principal(pub Option<String>);
pub async fn auth<S: Storage + 'static, P: Provider + 'static>(
State(state): State<AppState<S, P>>,
mut request: Request,
next: Next,
) -> Response {
if state.config.admin_token.is_none()
&& state
.key_map
.read()
.unwrap_or_else(|e| e.into_inner())
.is_empty()
{
request.extensions_mut().insert(Principal(None));
return next.run(request).await;
}
let headers = request.headers();
let bearer = headers
.get("authorization")
.and_then(|v| v.to_str().ok())
.and_then(|h| h.strip_prefix("Bearer "));
let x_api_key = headers.get("x-api-key").and_then(|v| v.to_str().ok());
let token = match bearer.or(x_api_key) {
Some(t) => t,
None => {
return (
StatusCode::UNAUTHORIZED,
Json(ApiError::new(
"missing Authorization or x-api-key header",
"authentication_error",
)),
)
.into_response();
}
};
let principal = state
.key_map
.read()
.unwrap_or_else(|e| e.into_inner())
.get(token)
.cloned();
let Some(principal) = principal else {
return (
StatusCode::UNAUTHORIZED,
Json(ApiError::new("invalid API key", "authentication_error")),
)
.into_response();
};
request.extensions_mut().insert(Principal(Some(principal)));
next.run(request).await
}