use crate::auth::tokens::hash_api_token;
use crate::pool::Pool;
use axum::body::Body;
use axum::http::{Request, header::AUTHORIZATION};
use axum::middleware::Next;
use axum::response::Response;
#[derive(Clone, Copy, Debug)]
pub struct ApiKeyUser {
pub user_id: i64,
pub key_id: i64,
}
pub(crate) async fn bearer_auth(pool: Pool, mut req: Request<Body>, next: Next) -> Response {
if let Some(token) = req
.headers()
.get(AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "))
.map(|s| s.trim())
.filter(|s| !s.is_empty())
{
let hash = hash_api_token(token);
let row: Result<Option<(i64, i64)>, _> = sqlx::query_as(
"SELECT id, user_id FROM api_keys WHERE token_hash = $1 AND revoked_at IS NULL",
)
.bind(&hash)
.fetch_optional(&pool)
.await;
match row {
Ok(Some((key_id, user_id))) => {
req.extensions_mut().insert(ApiKeyUser { user_id, key_id });
let p = pool.clone();
tokio::spawn(async move {
if let Err(e) = sqlx::query(
"UPDATE api_keys SET last_used_at = CURRENT_TIMESTAMP WHERE id = $1",
)
.bind(key_id)
.execute(&p)
.await
{
eprintln!("[api_key] WARN: last_used_at update failed (key {key_id}): {e}");
}
});
}
Ok(None) => {}
Err(e) => eprintln!("[api_key] WARN: api_keys lookup failed: {e}"),
}
}
next.run(req).await
}