use std::sync::Arc;
use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use cratefield_core::Problem;
use crate::jwks::AuthClient;
use crate::verify::{Claims, token_fingerprint};
#[derive(Clone)]
pub struct AuthState(pub Arc<AuthClient>);
#[derive(Debug, Clone)]
pub struct Authenticated(pub Claims);
impl<S> FromRequestParts<S> for Authenticated
where
AuthState: axum::extract::FromRef<S>,
S: Send + Sync,
{
type Rejection = Problem;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let AuthState(client) = axum::extract::FromRef::from_ref(state);
let token = bearer(parts).ok_or_else(unauthorized)?;
match client.verify(token).await {
Ok(claims) => Ok(Self(claims)),
Err(err) => {
tracing::info!(
reason = %err,
token = %token_fingerprint(token),
"rejected a bearer token"
);
Err(unauthorized())
}
}
}
}
fn bearer(parts: &Parts) -> Option<&str> {
let value = parts
.headers
.get(axum::http::header::AUTHORIZATION)?
.to_str()
.ok()?;
let rest = value
.strip_prefix("Bearer ")
.or_else(|| value.strip_prefix("bearer "))?;
(!rest.is_empty()).then_some(rest)
}
pub const UNAUTHENTICATED: cratefield_core::ProblemDef = cratefield_core::SLUGS.unauthenticated;
fn unauthorized() -> Problem {
Problem::new(&UNAUTHENTICATED)
}