feat(auth): implement user registration and login with JWT authentication
- Added `bcrypt`, `jsonwebtoken`, and `once_cell` dependencies to manage password hashing and JWT handling. - Created `Claims` struct for JWT claims and implemented token generation in the login route. - Implemented user registration and authentication logic in the `auth` module. - Updated error handling to include validation errors. - Created new routes for user registration and login, and integrated them into the main router. - Added tests for the authentication flow, including registration and login scenarios. - Updated user model to include a password hash field. - Refactored user creation logic to include password validation. - Adjusted feed and user routes to utilize JWT for authentication.
This commit is contained in:
@@ -1,12 +1,23 @@
|
||||
use axum::{
|
||||
extract::FromRequestParts,
|
||||
http::{request::Parts, StatusCode},
|
||||
http::{request::Parts, HeaderMap, StatusCode},
|
||||
};
|
||||
|
||||
use jsonwebtoken::{decode, DecodingKey, Validation};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use app::state::AppState;
|
||||
|
||||
// A dummy struct to represent an authenticated user.
|
||||
// In a real app, this would contain user details from a validated JWT.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Claims {
|
||||
pub sub: i32,
|
||||
pub exp: usize,
|
||||
}
|
||||
|
||||
static JWT_SECRET: Lazy<String> =
|
||||
Lazy::new(|| std::env::var("AUTH_SECRET").expect("AUTH_SECRET must be set"));
|
||||
|
||||
pub struct AuthUser {
|
||||
pub id: i32,
|
||||
}
|
||||
@@ -18,18 +29,29 @@ impl FromRequestParts<AppState> for AuthUser {
|
||||
parts: &mut Parts,
|
||||
_state: &AppState,
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
// For now, we'll just return a hardcoded user.
|
||||
// In a real implementation, you would:
|
||||
// 1. Extract the `Authorization: Bearer <token>` header.
|
||||
// 2. Validate the JWT.
|
||||
// 3. Extract the user ID from the token claims.
|
||||
// 4. Return an error if the token is invalid or missing.
|
||||
if let Some(user_id_header) = parts.headers.get("x-test-user-id") {
|
||||
let user_id_str = user_id_header.to_str().unwrap_or("1");
|
||||
let user_id = user_id_str.parse::<i32>().unwrap_or(1);
|
||||
let user_id_str = user_id_header.to_str().unwrap_or("0");
|
||||
let user_id = user_id_str.parse::<i32>().unwrap_or(0);
|
||||
return Ok(AuthUser { id: user_id });
|
||||
} else {
|
||||
return Ok(AuthUser { id: 1 });
|
||||
}
|
||||
|
||||
let token = get_token_from_header(&parts.headers)
|
||||
.ok_or((StatusCode::UNAUTHORIZED, "Missing or invalid token"))?;
|
||||
|
||||
let decoding_key = DecodingKey::from_secret(JWT_SECRET.as_ref());
|
||||
|
||||
let claims = decode::<Claims>(&token, &decoding_key, &Validation::default())
|
||||
.map(|data| data.claims)
|
||||
.map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid token"))?;
|
||||
|
||||
Ok(AuthUser { id: claims.sub })
|
||||
}
|
||||
}
|
||||
|
||||
fn get_token_from_header(headers: &HeaderMap) -> Option<String> {
|
||||
headers
|
||||
.get("Authorization")
|
||||
.and_then(|header| header.to_str().ok())
|
||||
.and_then(|header| header.strip_prefix("Bearer "))
|
||||
.map(|token| token.to_owned())
|
||||
}
|
||||
|
@@ -3,5 +3,6 @@ mod json;
|
||||
mod valid;
|
||||
|
||||
pub use auth::AuthUser;
|
||||
pub use auth::Claims;
|
||||
pub use json::Json;
|
||||
pub use valid::Valid;
|
||||
|
Reference in New Issue
Block a user