init
This commit is contained in:
109
notes-api/src/routes/auth.rs
Normal file
109
notes-api/src/routes/auth.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
//! Authentication routes
|
||||
|
||||
use axum::{Json, extract::State, http::StatusCode};
|
||||
use axum_login::AuthSession;
|
||||
use validator::Validate;
|
||||
|
||||
use notes_domain::User;
|
||||
use password_auth::generate_hash;
|
||||
|
||||
use crate::auth::{AuthBackend, AuthUser, Credentials};
|
||||
use crate::dto::{LoginRequest, RegisterRequest};
|
||||
use crate::error::{ApiError, ApiResult};
|
||||
use crate::state::AppState;
|
||||
|
||||
/// Register a new user
|
||||
pub async fn register(
|
||||
State(state): State<AppState>,
|
||||
mut auth_session: AuthSession<AuthBackend>,
|
||||
Json(payload): Json<RegisterRequest>,
|
||||
) -> ApiResult<StatusCode> {
|
||||
payload
|
||||
.validate()
|
||||
.map_err(|e| ApiError::validation(e.to_string()))?;
|
||||
|
||||
// Check if user exists
|
||||
if state
|
||||
.user_repo
|
||||
.find_by_email(&payload.email)
|
||||
.await
|
||||
.map_err(ApiError::from)?
|
||||
.is_some()
|
||||
{
|
||||
return Err(ApiError::Domain(
|
||||
notes_domain::DomainError::UserAlreadyExists(payload.email.clone()),
|
||||
));
|
||||
}
|
||||
|
||||
// Hash password
|
||||
let password_hash = generate_hash(&payload.password);
|
||||
|
||||
// Create use
|
||||
// For local registration, we use email as subject
|
||||
let user = User::new_local(&payload.email, &password_hash);
|
||||
|
||||
state.user_repo.save(&user).await.map_err(ApiError::from)?;
|
||||
|
||||
// Auto login after registration
|
||||
let user = AuthUser(user);
|
||||
auth_session
|
||||
.login(&user)
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(e.to_string()))?;
|
||||
|
||||
Ok(StatusCode::CREATED)
|
||||
}
|
||||
|
||||
/// Login user
|
||||
pub async fn login(
|
||||
mut auth_session: AuthSession<AuthBackend>,
|
||||
Json(payload): Json<LoginRequest>,
|
||||
) -> ApiResult<StatusCode> {
|
||||
payload
|
||||
.validate()
|
||||
.map_err(|e| ApiError::validation(e.to_string()))?;
|
||||
|
||||
let user = auth_session
|
||||
.authenticate(Credentials {
|
||||
email: payload.email,
|
||||
password: payload.password,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(e.to_string()))?
|
||||
.ok_or_else(|| ApiError::validation("Invalid email or password"))?; // Generic error for security
|
||||
|
||||
auth_session
|
||||
.login(&user)
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(e.to_string()))?;
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
/// Logout user
|
||||
pub async fn logout(mut auth_session: AuthSession<AuthBackend>) -> ApiResult<StatusCode> {
|
||||
auth_session
|
||||
.logout()
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(e.to_string()))?;
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
/// Get current user
|
||||
pub async fn me(
|
||||
auth_session: AuthSession<AuthBackend>,
|
||||
) -> ApiResult<Json<crate::dto::UserResponse>> {
|
||||
let user =
|
||||
auth_session
|
||||
.user
|
||||
.ok_or(ApiError::Domain(notes_domain::DomainError::Unauthorized(
|
||||
"Not logged in".to_string(),
|
||||
)))?;
|
||||
|
||||
Ok(Json(crate::dto::UserResponse {
|
||||
id: user.0.id,
|
||||
email: user.0.email.clone(),
|
||||
created_at: user.0.created_at,
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user