fix(auth): make AuthenticatedUser extractor perform authentication directly
Co-authored-by: jnsgruk <668505+jnsgruk@users.noreply.github.com>
This commit is contained in:
parent
5f4cbf5294
commit
5c05f35a59
1 changed files with 53 additions and 10 deletions
|
|
@ -15,18 +15,61 @@ use crate::server::server::AppState;
|
||||||
pub struct AuthenticatedUser(pub User);
|
pub struct AuthenticatedUser(pub User);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<S> FromRequestParts<S> for AuthenticatedUser
|
impl FromRequestParts<AppState> for AuthenticatedUser {
|
||||||
where
|
|
||||||
S: Send + Sync,
|
|
||||||
{
|
|
||||||
type Rejection = StatusCode;
|
type Rejection = StatusCode;
|
||||||
|
|
||||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
async fn from_request_parts(
|
||||||
parts
|
parts: &mut Parts,
|
||||||
.extensions
|
state: &AppState,
|
||||||
.get::<AuthenticatedUser>()
|
) -> Result<Self, Self::Rejection> {
|
||||||
.cloned()
|
// Try to get from extensions first (if middleware already set it)
|
||||||
.ok_or(StatusCode::UNAUTHORIZED)
|
if let Some(user) = parts.extensions.get::<AuthenticatedUser>() {
|
||||||
|
return Ok(user.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, extract from Authorization header directly
|
||||||
|
let auth_header = parts
|
||||||
|
.headers
|
||||||
|
.get(header::AUTHORIZATION)
|
||||||
|
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||||
|
|
||||||
|
let auth_str = auth_header.to_str().map_err(|_| StatusCode::UNAUTHORIZED)?;
|
||||||
|
|
||||||
|
// Check for "Bearer <token>" format
|
||||||
|
let token = auth_str
|
||||||
|
.strip_prefix("Bearer ")
|
||||||
|
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||||
|
|
||||||
|
// Hash the token to look it up in the database
|
||||||
|
let token_hash = hash_token(token);
|
||||||
|
|
||||||
|
// Look up the token
|
||||||
|
let token_record = state
|
||||||
|
.token_repo
|
||||||
|
.get_by_token_hash(&token_hash)
|
||||||
|
.await
|
||||||
|
.map_err(|_| StatusCode::UNAUTHORIZED)?;
|
||||||
|
|
||||||
|
// Check if token is revoked
|
||||||
|
if token_record.is_revoked() {
|
||||||
|
return Err(StatusCode::UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last used timestamp (fire and forget)
|
||||||
|
let token_repo = state.token_repo.clone();
|
||||||
|
let token_id = token_record.id.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let _ = token_repo.update_last_used(token_id).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the user
|
||||||
|
let user = state
|
||||||
|
.user_repo
|
||||||
|
.get(token_record.user_id)
|
||||||
|
.await
|
||||||
|
.map_err(|_| StatusCode::UNAUTHORIZED)?;
|
||||||
|
|
||||||
|
Ok(AuthenticatedUser(user))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue