Phase 2.3: JWT Authentication implementation

- Implemented JWT-based authentication system with access and refresh tokens
- Added password hashing service using PBKDF2
- Created authentication handlers: register, login, refresh, logout
- Added protected routes with JWT middleware
- Created user profile handlers
- Fixed all compilation errors
- Added integration tests for authentication endpoints
- Added reqwest dependency for testing
- Created test script and environment example documentation

All changes:
- backend/src/auth/: Complete auth module (JWT, password, claims)
- backend/src/handlers/: Auth, users, and health handlers
- backend/src/middleware/: JWT authentication middleware
- backend/src/config/: Added AppState with Clone derive
- backend/src/main.rs: Fixed imports and added auth routes
- backend/src/db/mod.rs: Changed error handling to anyhow::Result
- backend/Cargo.toml: Added reqwest for testing
- backend/tests/auth_tests.rs: Integration tests
- thoughts/: Documentation updates (STATUS.md, env.example, test_auth.sh)
This commit is contained in:
goose 2026-02-14 20:03:11 -03:00
parent 154c3d1152
commit 8b2c13501f
19 changed files with 935 additions and 98 deletions

View file

@ -0,0 +1,51 @@
use axum::{
extract::{Request, State},
http::StatusCode,
middleware::Next,
response::Response,
};
use crate::auth::claims::AccessClaims;
use crate::config::AppState;
pub async fn jwt_auth_middleware(
State(state): State<AppState>,
mut req: Request,
next: Next,
) -> Result<Response, StatusCode> {
let headers = req.headers();
// Extract Authorization header
let auth_header = headers
.get("Authorization")
.and_then(|h| h.to_str().ok())
.ok_or(StatusCode::UNAUTHORIZED)?;
// Check Bearer token format
if !auth_header.starts_with("Bearer ") {
return Err(StatusCode::UNAUTHORIZED);
}
let token = &auth_header[7..]; // Remove "Bearer " prefix
// Verify token
let claims = state
.jwt_service
.verify_access_token(token)
.map_err(|_| StatusCode::UNAUTHORIZED)?;
// Add claims to request extensions for handlers to use
req.extensions_mut().insert(claims);
Ok(next.run(req).await)
}
// Extension method to extract claims from request
pub trait RequestClaimsExt {
fn claims(&self) -> Option<&AccessClaims>;
}
impl RequestClaimsExt for Request {
fn claims(&self) -> Option<&AccessClaims> {
self.extensions().get::<AccessClaims>()
}
}

View file

@ -0,0 +1 @@
pub mod auth;