diff --git a/src/server/auth.rs b/src/server/auth.rs index 8f07aba..9c366a8 100644 --- a/src/server/auth.rs +++ b/src/server/auth.rs @@ -15,18 +15,61 @@ use crate::server::server::AppState; pub struct AuthenticatedUser(pub User); #[async_trait] -impl FromRequestParts for AuthenticatedUser -where - S: Send + Sync, -{ +impl FromRequestParts for AuthenticatedUser { type Rejection = StatusCode; - async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - parts - .extensions - .get::() - .cloned() - .ok_or(StatusCode::UNAUTHORIZED) + async fn from_request_parts( + parts: &mut Parts, + state: &AppState, + ) -> Result { + // Try to get from extensions first (if middleware already set it) + if let Some(user) = parts.extensions.get::() { + 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 " 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)) } }