2026-01-27 16:21:58 +00:00
|
|
|
use axum::{
|
|
|
|
|
extract::FromRequestParts,
|
|
|
|
|
http::{request::Parts, StatusCode},
|
|
|
|
|
};
|
2026-01-28 23:46:43 +00:00
|
|
|
use std::sync::Arc;
|
2026-01-27 16:21:58 +00:00
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
use super::jwt::{verify_token, Claims};
|
2026-01-28 23:46:43 +00:00
|
|
|
use crate::config::Config;
|
2026-01-27 16:21:58 +00:00
|
|
|
|
|
|
|
|
pub struct AuthUser {
|
|
|
|
|
pub user_id: Uuid,
|
|
|
|
|
pub username: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S> FromRequestParts<S> for AuthUser
|
|
|
|
|
where
|
|
|
|
|
S: Send + Sync,
|
|
|
|
|
{
|
|
|
|
|
type Rejection = (StatusCode, &'static str);
|
|
|
|
|
|
|
|
|
|
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
|
|
|
|
let auth_header = parts
|
|
|
|
|
.headers
|
|
|
|
|
.get("Authorization")
|
|
|
|
|
.and_then(|value| value.to_str().ok())
|
|
|
|
|
.ok_or((StatusCode::UNAUTHORIZED, "Missing authorization header"))?;
|
|
|
|
|
|
2026-02-03 16:54:39 +00:00
|
|
|
let token = auth_header.strip_prefix("Bearer ").ok_or((
|
|
|
|
|
StatusCode::UNAUTHORIZED,
|
|
|
|
|
"Invalid authorization header format",
|
|
|
|
|
))?;
|
2026-01-27 16:21:58 +00:00
|
|
|
|
2026-01-28 23:46:43 +00:00
|
|
|
let secret = parts
|
|
|
|
|
.extensions
|
|
|
|
|
.get::<Arc<Config>>()
|
|
|
|
|
.map(|c| c.jwt_secret.clone())
|
|
|
|
|
.or_else(|| std::env::var("JWT_SECRET").ok())
|
|
|
|
|
.unwrap_or_else(|| "dev-secret-change-in-production".to_string());
|
2026-01-27 16:21:58 +00:00
|
|
|
|
|
|
|
|
let claims: Claims = verify_token(token, &secret)
|
|
|
|
|
.map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid token"))?;
|
|
|
|
|
|
|
|
|
|
Ok(AuthUser {
|
|
|
|
|
user_id: claims.sub,
|
|
|
|
|
username: claims.username,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|