salvor_server/auth.rs
1//! Bearer-token auth, the whole of it.
2//!
3//! The posture is single-tenant: either a shared secret
4//! guards every endpoint, or the server trusts its caller and a reverse proxy
5//! owns auth. There is no user model and no RBAC.
6//!
7//! When [`AppState::auth_token`](crate::AppState::auth_token) is set, this
8//! middleware requires `Authorization: Bearer <that token>` on every request
9//! and answers anything else with a `401` carrying the standard error
10//! envelope. When it is unset, the middleware is a pass-through.
11
12use axum::extract::{Request, State};
13use axum::http::header;
14use axum::middleware::Next;
15use axum::response::{IntoResponse, Response};
16
17use crate::error::ApiError;
18use crate::state::AppState;
19
20/// Rejects a request whose bearer token is missing or wrong, when a token is
21/// configured; otherwise passes it straight through.
22pub async fn require_bearer(
23 State(state): State<AppState>,
24 request: Request,
25 next: Next,
26) -> Response {
27 let Some(expected) = state.auth_token() else {
28 return next.run(request).await;
29 };
30 let presented = request
31 .headers()
32 .get(header::AUTHORIZATION)
33 .and_then(|value| value.to_str().ok());
34 if presented == Some(&format!("Bearer {expected}")) {
35 next.run(request).await
36 } else {
37 ApiError::Unauthorized.into_response()
38 }
39}