From 4c3e54dacaaf480751ecdd3e9df324f129c7375e Mon Sep 17 00:00:00 2001 From: 409 <409dev@protonmail.com> Date: Fri, 18 Jul 2025 14:05:19 +0200 Subject: [PATCH] get complete session from login request instead of fetching again --- .../lib/domain/warren/models/user/requests.rs | 24 +++++++++- backend/src/lib/domain/warren/ports/mod.rs | 7 ++- .../src/lib/domain/warren/ports/notifier.rs | 4 +- backend/src/lib/domain/warren/service/auth.rs | 15 ++++-- .../http/handlers/auth/fetch_session.rs | 31 ++---------- .../lib/inbound/http/handlers/auth/login.rs | 18 ++++--- backend/src/lib/inbound/http/handlers/mod.rs | 26 ++++++++++ .../src/lib/outbound/notifier_debug_logger.rs | 6 +-- frontend/lib/api/auth/login.ts | 47 ++++++++----------- 9 files changed, 102 insertions(+), 76 deletions(-) diff --git a/backend/src/lib/domain/warren/models/user/requests.rs b/backend/src/lib/domain/warren/models/user/requests.rs index 86ec9e1..a7e8d25 100644 --- a/backend/src/lib/domain/warren/models/user/requests.rs +++ b/backend/src/lib/domain/warren/models/user/requests.rs @@ -1,8 +1,8 @@ use thiserror::Error; -use crate::domain::warren::models::auth_session::requests::CreateAuthSessionError; +use crate::domain::warren::models::auth_session::{AuthSession, requests::CreateAuthSessionError}; -use super::{UserEmail, UserName}; +use super::{User, UserEmail, UserName}; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RegisterUserRequest { @@ -125,6 +125,26 @@ pub struct LoginUserRequest { password: UserPassword, } +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct LoginUserResponse { + session: AuthSession, + user: User, +} + +impl LoginUserResponse { + pub fn new(session: AuthSession, user: User) -> Self { + Self { session, user } + } + + pub fn session(&self) -> &AuthSession { + &self.session + } + + pub fn user(&self) -> &User { + &self.user + } +} + impl LoginUserRequest { pub fn new(email: UserEmail, password: UserPassword) -> Self { Self { email, password } diff --git a/backend/src/lib/domain/warren/ports/mod.rs b/backend/src/lib/domain/warren/ports/mod.rs index 7a80091..040112d 100644 --- a/backend/src/lib/domain/warren/ports/mod.rs +++ b/backend/src/lib/domain/warren/ports/mod.rs @@ -19,7 +19,10 @@ use super::models::{ DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError, DeleteFileRequest, File, FilePath, ListFilesError, ListFilesRequest, RenameEntryError, RenameEntryRequest, }, - user::{LoginUserError, LoginUserRequest, RegisterUserError, RegisterUserRequest, User}, + user::{ + LoginUserError, LoginUserRequest, LoginUserResponse, RegisterUserError, + RegisterUserRequest, User, + }, warren::{ CreateWarrenDirectoryError, CreateWarrenDirectoryRequest, DeleteWarrenDirectoryError, DeleteWarrenDirectoryRequest, DeleteWarrenFileError, DeleteWarrenFileRequest, @@ -108,7 +111,7 @@ pub trait AuthService: Clone + Send + Sync + 'static { fn login_user( &self, request: LoginUserRequest, - ) -> impl Future> + Send; + ) -> impl Future> + Send; fn create_auth_session( &self, request: CreateAuthSessionRequest, diff --git a/backend/src/lib/domain/warren/ports/notifier.rs b/backend/src/lib/domain/warren/ports/notifier.rs index 51ce7ea..2518dd2 100644 --- a/backend/src/lib/domain/warren/ports/notifier.rs +++ b/backend/src/lib/domain/warren/ports/notifier.rs @@ -3,7 +3,7 @@ use uuid::Uuid; use crate::domain::warren::models::{ auth_session::requests::FetchAuthSessionResponse, file::{AbsoluteFilePath, File, FilePath}, - user::User, + user::{LoginUserResponse, User}, warren::Warren, }; @@ -75,7 +75,7 @@ pub trait FileSystemNotifier: Clone + Send + Sync + 'static { pub trait AuthNotifier: Clone + Send + Sync + 'static { fn user_registered(&self, user: &User) -> impl Future + Send; - fn user_logged_in(&self, user: &User) -> impl Future + Send; + fn user_logged_in(&self, response: &LoginUserResponse) -> impl Future + Send; fn auth_session_created(&self, user_id: &Uuid) -> impl Future + Send; fn auth_session_fetched( &self, diff --git a/backend/src/lib/domain/warren/service/auth.rs b/backend/src/lib/domain/warren/service/auth.rs index 1790b09..71152ea 100644 --- a/backend/src/lib/domain/warren/service/auth.rs +++ b/backend/src/lib/domain/warren/service/auth.rs @@ -10,7 +10,8 @@ use crate::{ }, }, user::{ - LoginUserError, LoginUserRequest, RegisterUserError, RegisterUserRequest, User, + LoginUserError, LoginUserRequest, LoginUserResponse, RegisterUserError, + RegisterUserRequest, User, }, }, ports::{AuthMetrics, AuthNotifier, AuthRepository, AuthService}, @@ -93,7 +94,10 @@ where result } - async fn login_user(&self, request: LoginUserRequest) -> Result { + async fn login_user( + &self, + request: LoginUserRequest, + ) -> Result { let user = self .repository .verify_user_password(request.into()) @@ -105,11 +109,12 @@ where user.clone(), self.config.session_lifetime(), )) - .await; + .await + .map(|session| LoginUserResponse::new(session, user)); - if result.as_ref().is_ok() { + if let Ok(response) = result.as_ref() { self.metrics.record_user_login_success().await; - self.notifier.user_logged_in(&user).await; + self.notifier.user_logged_in(response).await; } else { self.metrics.record_user_login_failure().await; } diff --git a/backend/src/lib/inbound/http/handlers/auth/fetch_session.rs b/backend/src/lib/inbound/http/handlers/auth/fetch_session.rs index e9eda5c..0a099d4 100644 --- a/backend/src/lib/inbound/http/handlers/auth/fetch_session.rs +++ b/backend/src/lib/inbound/http/handlers/auth/fetch_session.rs @@ -3,45 +3,22 @@ use axum::{ http::{HeaderMap, StatusCode}, }; use serde::Serialize; -use uuid::Uuid; use crate::{ domain::warren::{ - models::{ - auth_session::{ - AuthSessionId, - requests::{FetchAuthSessionRequest, FetchAuthSessionResponse}, - }, - user::User, + models::auth_session::{ + AuthSessionId, + requests::{FetchAuthSessionRequest, FetchAuthSessionResponse}, }, ports::{AuthService, WarrenService}, }, inbound::http::{ AppState, + handlers::SessionUser, responses::{ApiError, ApiSuccess}, }, }; -#[derive(Debug, Clone, Serialize, PartialEq)] -#[serde(rename_all = "camelCase")] -struct SessionUser { - id: Uuid, - name: String, - email: String, - admin: bool, -} - -impl From for SessionUser { - fn from(value: User) -> Self { - Self { - id: *value.id(), - name: value.name().to_string(), - email: value.email().to_string(), - admin: value.admin(), - } - } -} - #[derive(Debug, Clone, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct FetchSessionResponseBody { diff --git a/backend/src/lib/inbound/http/handlers/auth/login.rs b/backend/src/lib/inbound/http/handlers/auth/login.rs index 34e8ea2..398741f 100644 --- a/backend/src/lib/inbound/http/handlers/auth/login.rs +++ b/backend/src/lib/inbound/http/handlers/auth/login.rs @@ -4,14 +4,15 @@ use thiserror::Error; use crate::{ domain::warren::{ - models::{ - auth_session::AuthSession, - user::{LoginUserRequest, UserEmail, UserEmailError, UserPassword, UserPasswordError}, + models::user::{ + LoginUserRequest, LoginUserResponse, UserEmail, UserEmailError, UserPassword, + UserPasswordError, }, ports::{AuthService, WarrenService}, }, inbound::http::{ AppState, + handlers::SessionUser, responses::{ApiError, ApiSuccess}, }, }; @@ -66,16 +67,19 @@ impl LoginUserHttpRequestBody { } } -// TODO: Include `user` and `expires_at` fields #[derive(Debug, Clone, PartialEq, Serialize)] pub struct LoginResponseBody { token: String, + user: SessionUser, + expires_at: i64, } -impl From for LoginResponseBody { - fn from(value: AuthSession) -> Self { +impl From for LoginResponseBody { + fn from(value: LoginUserResponse) -> Self { Self { - token: value.session_id().to_string(), + token: value.session().session_id().to_string(), + user: value.user().to_owned().into(), + expires_at: value.session().expires_at().and_utc().timestamp_millis(), } } } diff --git a/backend/src/lib/inbound/http/handlers/mod.rs b/backend/src/lib/inbound/http/handlers/mod.rs index 1751879..47b72be 100644 --- a/backend/src/lib/inbound/http/handlers/mod.rs +++ b/backend/src/lib/inbound/http/handlers/mod.rs @@ -1,2 +1,28 @@ +use serde::Serialize; +use uuid::Uuid; + +use crate::domain::warren::models::user::User; + pub mod auth; pub mod warrens; + +#[derive(Debug, Clone, Serialize, PartialEq)] +#[serde(rename_all = "camelCase")] +/// A session user that can be safely sent to the client +pub struct SessionUser { + id: Uuid, + name: String, + email: String, + admin: bool, +} + +impl From for SessionUser { + fn from(value: User) -> Self { + Self { + id: *value.id(), + name: value.name().to_string(), + email: value.email().to_string(), + admin: value.admin(), + } + } +} diff --git a/backend/src/lib/outbound/notifier_debug_logger.rs b/backend/src/lib/outbound/notifier_debug_logger.rs index 47a5634..e6272c8 100644 --- a/backend/src/lib/outbound/notifier_debug_logger.rs +++ b/backend/src/lib/outbound/notifier_debug_logger.rs @@ -4,7 +4,7 @@ use crate::domain::warren::{ models::{ auth_session::requests::FetchAuthSessionResponse, file::{File, FilePath}, - user::User, + user::{LoginUserResponse, User}, warren::Warren, }, ports::{AuthNotifier, FileSystemNotifier, WarrenNotifier}, @@ -122,8 +122,8 @@ impl AuthNotifier for NotifierDebugLogger { tracing::debug!("[Notifier] Registered user {}", user.name()); } - async fn user_logged_in(&self, user: &User) { - tracing::debug!("[Notifier] Logged in user {}", user.name()); + async fn user_logged_in(&self, response: &LoginUserResponse) { + tracing::debug!("[Notifier] Logged in user {}", response.user().name()); } async fn auth_session_created(&self, user_id: &Uuid) { diff --git a/frontend/lib/api/auth/login.ts b/frontend/lib/api/auth/login.ts index 6a6117c..344b695 100644 --- a/frontend/lib/api/auth/login.ts +++ b/frontend/lib/api/auth/login.ts @@ -1,25 +1,25 @@ import { toast } from 'vue-sonner'; import type { ApiResponse } from '~/types/api'; import { getAuthSessionData } from './getSession'; +import type { AuthUser } from '~/types/auth'; export async function loginUser( email: string, password: string ): Promise<{ success: boolean }> { - const { data, error } = await useFetch>( - getApiUrl('auth/login'), - { - method: 'POST', - headers: { - 'content-type': 'application/json', - }, - body: JSON.stringify({ - email: email, - password: password, - }), - responseType: 'json', - } - ); + const { data, error } = await useFetch< + ApiResponse<{ token: string; user: AuthUser; expiresAt: number }> + >(getApiUrl('auth/login'), { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify({ + email: email, + password: password, + }), + responseType: 'json', + }); if (data.value == null) { toast.error('Login', { @@ -31,23 +31,14 @@ export async function loginUser( }; } - // TODO: Get this data directly from the login request - const sessionData = await getAuthSessionData({ - sessionType: 'WarrenAuth', - sessionId: data.value.data.token, - }); - - if (!sessionData.success) { - return { - success: false, - }; - } + const token = data.value.data.token; + const { user, expiresAt } = data.value.data; useAuthSession().value = { type: 'WarrenAuth', - id: data.value.data.token, - user: sessionData.user, - expiresAt: sessionData.expiresAt, + id: token, + user, + expiresAt, }; toast.success('Login', {