quarlus_security/
extractor.rs1use std::sync::Arc;
2
3use quarlus_core::http::extract::{FromRef, FromRequestParts};
4use quarlus_core::http::header::{Parts, AUTHORIZATION};
5use tracing::{debug, warn};
6
7use crate::error::SecurityError;
8use crate::identity::{AuthenticatedUser, IdentityBuilder};
9use crate::jwt::JwtValidator;
10
11fn extract_bearer_token(header_value: &str) -> Result<&str, SecurityError> {
13 let parts: Vec<&str> = header_value.splitn(2, ' ').collect();
14 if parts.len() != 2 {
15 return Err(SecurityError::InvalidAuthScheme);
16 }
17 if !parts[0].eq_ignore_ascii_case("Bearer") {
18 return Err(SecurityError::InvalidAuthScheme);
19 }
20 Ok(parts[1])
21}
22
23pub async fn extract_jwt_identity<S, B>(
48 parts: &mut Parts,
49 state: &S,
50) -> Result<B::Identity, quarlus_core::AppError>
51where
52 S: Send + Sync,
53 B: IdentityBuilder + 'static,
54 Arc<JwtValidator<B>>: FromRef<S>,
55{
56 let auth_header = parts.headers.get(AUTHORIZATION).ok_or_else(|| {
58 warn!(uri = %parts.uri, "Missing Authorization header");
59 SecurityError::MissingAuthHeader
60 })?;
61
62 let auth_value = auth_header
63 .to_str()
64 .map_err(|_| SecurityError::InvalidAuthScheme)?;
65
66 let token = extract_bearer_token(auth_value)?;
68
69 let validator: Arc<JwtValidator<B>> = Arc::from_ref(state);
71
72 let identity = validator.validate(token).await.map_err(|e| {
74 warn!(uri = %parts.uri, error = %e, "JWT validation failed");
75 quarlus_core::AppError::from(e)
76 })?;
77
78 debug!(uri = %parts.uri, "Authenticated request");
79 Ok(identity)
80}
81
82impl<S> FromRequestParts<S> for AuthenticatedUser
101where
102 S: Send + Sync,
103 Arc<JwtValidator>: quarlus_core::http::extract::FromRef<S>,
104{
105 type Rejection = quarlus_core::AppError;
106
107 async fn from_request_parts(
108 parts: &mut Parts,
109 state: &S,
110 ) -> Result<Self, Self::Rejection> {
111 extract_jwt_identity::<S, crate::identity::DefaultIdentityBuilder>(parts, state).await
112 }
113}