use std::sync::Arc;
use axum::{
extract::FromRequestParts,
http::{request::Parts, StatusCode},
response::{IntoResponse, Response},
};
use crate::core::UssoAuth;
use crate::schemas::UserData;
pub struct AuthenticatedUser(pub UserData);
pub struct OptionalUser(pub Option<UserData>);
fn extract_bearer_token(headers: &axum::http::HeaderMap) -> Option<String> {
let value = headers.get("Authorization")?.to_str().ok()?;
let token = value.strip_prefix("Bearer ")?.trim();
if token.is_empty() { None } else { Some(token.to_string()) }
}
impl<S> FromRequestParts<S> for AuthenticatedUser
where
S: Send + Sync,
{
type Rejection = Response;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let auth = parts
.extensions
.get::<Arc<UssoAuth>>()
.ok_or_else(|| (StatusCode::INTERNAL_SERVER_ERROR, "USSO auth not configured").into_response())?;
let token = extract_bearer_token(&parts.headers)
.ok_or_else(|| (StatusCode::UNAUTHORIZED, "missing token").into_response())?;
let user = auth
.user_data_from_token(&token, Some("access"))
.map_err(|_| (StatusCode::UNAUTHORIZED, "invalid token").into_response())?;
Ok(AuthenticatedUser(user))
}
}
impl<S> FromRequestParts<S> for OptionalUser
where
S: Send + Sync,
{
type Rejection = std::convert::Infallible;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let auth = match parts.extensions.get::<Arc<UssoAuth>>() {
Some(a) => a,
None => return Ok(OptionalUser(None)),
};
let token = match extract_bearer_token(&parts.headers) {
Some(t) => t,
None => return Ok(OptionalUser(None)),
};
let user = match auth.user_data_from_token(&token, Some("access")) {
Ok(u) => u,
Err(_) => return Ok(OptionalUser(None)),
};
Ok(OptionalUser(Some(user)))
}
}