use std::sync::Arc;
use quarlus_core::http::extract::{FromRef, FromRequestParts};
use quarlus_core::http::header::{Parts, AUTHORIZATION};
use tracing::{debug, warn};
use crate::error::SecurityError;
use crate::identity::{AuthenticatedUser, IdentityBuilder};
use crate::jwt::JwtValidator;
fn extract_bearer_token(header_value: &str) -> Result<&str, SecurityError> {
let parts: Vec<&str> = header_value.splitn(2, ' ').collect();
if parts.len() != 2 {
return Err(SecurityError::InvalidAuthScheme);
}
if !parts[0].eq_ignore_ascii_case("Bearer") {
return Err(SecurityError::InvalidAuthScheme);
}
Ok(parts[1])
}
pub async fn extract_jwt_identity<S, B>(
parts: &mut Parts,
state: &S,
) -> Result<B::Identity, quarlus_core::AppError>
where
S: Send + Sync,
B: IdentityBuilder + 'static,
Arc<JwtValidator<B>>: FromRef<S>,
{
let auth_header = parts.headers.get(AUTHORIZATION).ok_or_else(|| {
warn!(uri = %parts.uri, "Missing Authorization header");
SecurityError::MissingAuthHeader
})?;
let auth_value = auth_header
.to_str()
.map_err(|_| SecurityError::InvalidAuthScheme)?;
let token = extract_bearer_token(auth_value)?;
let validator: Arc<JwtValidator<B>> = Arc::from_ref(state);
let identity = validator.validate(token).await.map_err(|e| {
warn!(uri = %parts.uri, error = %e, "JWT validation failed");
quarlus_core::AppError::from(e)
})?;
debug!(uri = %parts.uri, "Authenticated request");
Ok(identity)
}
impl<S> FromRequestParts<S> for AuthenticatedUser
where
S: Send + Sync,
Arc<JwtValidator>: quarlus_core::http::extract::FromRef<S>,
{
type Rejection = quarlus_core::AppError;
async fn from_request_parts(
parts: &mut Parts,
state: &S,
) -> Result<Self, Self::Rejection> {
extract_jwt_identity::<S, crate::identity::DefaultIdentityBuilder>(parts, state).await
}
}