110 lines
2.8 KiB
Rust
110 lines
2.8 KiB
Rust
//! 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,
|
|
}))
|
|
}
|