use axum::{extract::FromRequestParts, http::request::Parts};
use axum_extra::{
TypedHeader,
headers::{Authorization, authorization::Bearer},
};
use crate::{claims::JwtClaims, jwks::JwksStore, validator::validate_token};
#[derive(Clone)]
pub struct AuthConfig {
pub issuer: String,
pub audience: Option<String>,
}
#[derive(Clone)]
pub struct Auth<T: JwtClaims>(pub T);
impl<S, T> FromRequestParts<S> for Auth<T>
where
S: Send + Sync + AsRef<JwksStore> + AsRef<AuthConfig>,
T: JwtClaims,
{
type Rejection = axum::http::StatusCode;
fn from_request_parts(
parts: &mut Parts,
state: &S,
) -> impl std::future::Future<Output = Result<Self, Self::Rejection>> + Send {
async move {
let TypedHeader(Authorization(bearer)) =
TypedHeader::<Authorization<Bearer>>::from_request_parts(parts, state)
.await
.map_err(|_| axum::http::StatusCode::UNAUTHORIZED)?;
let jwks_store: &JwksStore = state.as_ref();
let auth_config: &AuthConfig = state.as_ref();
let claims = validate_token::<T>(
bearer.token(),
jwks_store,
&auth_config.issuer,
auth_config.audience.as_deref(),
)
.await
.map_err(|_| axum::http::StatusCode::UNAUTHORIZED)?;
Ok(Auth(claims))
}
}
}