|
- //! Bearer-token authentication extractor.
- //!
- //! Use `AuthenticatedUser` as a handler parameter to require a valid JWT.
- //! Unauthenticated requests are rejected with `401 Unauthorized` before the
- //! handler body ever executes.
- //!
- //! # Example
- //!
- //! ```rust,ignore
- //! async fn protected_handler(
- //! user: AuthenticatedUser,
- //! State(state): State<AppState>,
- //! ) -> Result<impl IntoResponse, ApiError> {
- //! // user.id and user.roles are guaranteed valid here
- //! Ok(Json(json!({ "sub": user.id })))
- //! }
- //! ```
-
- use axum::{
- async_trait,
- extract::FromRequestParts,
- http::request::Parts,
- RequestPartsExt,
- };
- use axum_extra::{
- headers::{authorization::Bearer, Authorization},
- TypedHeader,
- };
- use uuid::Uuid;
-
- use crate::errors::ApiError;
-
- // ── Authenticated user claim ──────────────────────────────────────────────────
-
- /// Injected into handlers that require authentication.
- #[derive(Debug, Clone)]
- pub struct AuthenticatedUser {
- pub id: Uuid,
- pub roles: Vec<String>,
- }
-
- // ── FromRequestParts impl ─────────────────────────────────────────────────────
-
- #[async_trait]
- impl<S> FromRequestParts<S> for AuthenticatedUser
- where
- S: Send + Sync,
- {
- type Rejection = ApiError;
-
- async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
- // Extract the Bearer token from the Authorization header.
- let TypedHeader(Authorization(bearer)) = parts
- .extract::<TypedHeader<Authorization<Bearer>>>()
- .await
- .map_err(|_| ApiError::Unauthorized("Missing or malformed Authorization header".to_string()))?;
-
- // TODO: replace this stub with real JWT validation.
- //
- // Example using `jsonwebtoken`:
- // let secret = state.config().auth.jwt_secret.as_bytes();
- // let token_data = decode::<Claims>(bearer.token(), &DecodingKey::from_secret(secret), &Validation::default())?;
- // return Ok(AuthenticatedUser { id: token_data.claims.sub, roles: token_data.claims.roles });
-
- let _ = bearer.token(); // silence unused-variable lint on the stub
-
- Err(ApiError::Unauthorized("JWT validation not yet implemented".to_string()))
- }
- }
|