//! 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, mut auth_session: AuthSession, Json(payload): Json, ) -> ApiResult { 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, Json(payload): Json, ) -> ApiResult { 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) -> ApiResult { 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, ) -> ApiResult> { 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, })) }