use axum::{ extract::{FromRef, FromRequestParts}, http::{header, StatusCode}, }; use std::sync::Arc; use uuid::Uuid; use crate::AppState; use crate::api::auth::verify_access_token; #[derive(Debug, Clone)] pub struct AuthUser { pub user_id: Uuid, } impl FromRequestParts for AuthUser where S: Send + Sync, Arc: FromRef, { type Rejection = (StatusCode, axum::Json); fn from_request_parts<'life0, 'life1, 'async_trait>( parts: &'life0 mut axum::http::request::Parts, state: &'life1 S, ) -> ::core::pin::Pin> + ::core::marker::Send + 'async_trait, >> where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait, { Box::pin(async move { let state = Arc::::from_ref(state); let auth_header = parts .headers .get(header::AUTHORIZATION) .and_then(|v| v.to_str().ok()) .and_then(|v| v.strip_prefix("Bearer ")); match auth_header { Some(token) => match verify_access_token(token, &state.jwt_secret) { Ok(claims) => Ok(AuthUser { user_id: claims.sub }), Err(_) => Err(( StatusCode::UNAUTHORIZED, axum::Json(serde_json::json!({"error": "invalid or expired token"})), )), }, None => Err(( StatusCode::UNAUTHORIZED, axum::Json(serde_json::json!({"error": "missing authorization header"})), )), } }) } }