diff --git a/VEZA_INTEGRATION_PERFECTION_TODOLIST_TEMPLATE.json b/VEZA_INTEGRATION_PERFECTION_TODOLIST_TEMPLATE.json index de8de57f8..ce8bd82a3 100644 --- a/VEZA_INTEGRATION_PERFECTION_TODOLIST_TEMPLATE.json +++ b/VEZA_INTEGRATION_PERFECTION_TODOLIST_TEMPLATE.json @@ -412,7 +412,8 @@ "description": "Vérifier que AuthResponse correspond exactement à la réponse de /auth/login.", "priority": "P1", "priority_rank": 11, - "status": "todo", + "status": "completed", + "completed_at": "2025-01-27T14:15:00Z", "estimated_hours": 1, "side": "frontend_only", "files_to_modify": [ @@ -1092,13 +1093,13 @@ }, "progress_tracking": { "total_tasks": 32, - "completed": 10, + "completed": 11, "in_progress": 0, - "todo": 22, + "todo": 21, "blocked": 0, - "completion_percentage": 31, - "last_updated": "2025-01-27T14:00:00Z", + "completion_percentage": 34, + "last_updated": "2025-01-27T14:15:00Z", "estimated_completion_date": null, - "estimated_hours_remaining": 33.5 + "estimated_hours_remaining": 32.5 } } diff --git a/apps/web/src/features/auth/services/authService.ts b/apps/web/src/features/auth/services/authService.ts index 105c9ab3c..d86f973e1 100644 --- a/apps/web/src/features/auth/services/authService.ts +++ b/apps/web/src/features/auth/services/authService.ts @@ -7,6 +7,8 @@ import type { ResetPasswordFormData, } from '../types'; +// INT-TYPE-008: AuthResponse aligned with backend LoginResponse (dto.LoginResponse) +// Backend format: { user: UserResponse, token: TokenResponse, requires_2fa?: boolean } export interface AuthResponse { user: { id: string; @@ -18,6 +20,7 @@ export interface AuthResponse { refresh_token: string; expires_in: number; }; + requires_2fa?: boolean; // BE-API-001: Flag indicating 2FA is required } export interface ApiError { diff --git a/apps/web/src/features/auth/types/index.ts b/apps/web/src/features/auth/types/index.ts index d67686846..7ea04d2cc 100644 --- a/apps/web/src/features/auth/types/index.ts +++ b/apps/web/src/features/auth/types/index.ts @@ -25,9 +25,12 @@ export interface AuthTokens { expires_in: number; } +// INT-TYPE-008: AuthResponse aligned with backend LoginResponse (dto.LoginResponse) +// Backend format: { user: UserResponse, token: TokenResponse, requires_2fa?: boolean } export interface AuthResponse { - user: User; - token: AuthTokens; + user: User; // Matches backend UserResponse (id, email, username) + token: AuthTokens; // Matches backend TokenResponse (access_token, refresh_token, expires_in) + requires_2fa?: boolean; // BE-API-001: Flag indicating 2FA is required } export interface RefreshResponse { diff --git a/apps/web/src/services/api/auth.ts b/apps/web/src/services/api/auth.ts index 704e5559a..1d272ba5c 100644 --- a/apps/web/src/services/api/auth.ts +++ b/apps/web/src/services/api/auth.ts @@ -25,24 +25,29 @@ export interface RegisterRequest { } /** - * Types pour les réponses d'authentification - * Le backend retourne { success: true, data: { access_token, refresh_token, expires_in, token_type, user } } - * Le client API fait déjà l'unwrapping, donc response.data contient directement le contenu de data + * INT-TYPE-008: LoginResponse aligned with backend dto.LoginResponse + * Backend format: { user: UserResponse, token: TokenResponse, requires_2fa?: boolean } + * After unwrapping by apiClient: response.data = { user: {...}, token: { access_token, refresh_token, expires_in }, requires_2fa?: boolean } */ export interface LoginResponse { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - user: User; + user: User; // Matches backend UserResponse (id, email, username) + token: { + access_token: string; + refresh_token: string; + expires_in: number; + }; + requires_2fa?: boolean; // BE-API-001: Flag indicating 2FA is required } +// INT-TYPE-008: RegisterResponse aligned with backend dto.RegisterResponse +// Backend format: { user: UserResponse, token: TokenResponse } export interface RegisterResponse { - access_token?: string; - refresh_token?: string; - expires_in?: number; - token_type?: string; - user: User; + user: User; // Matches backend UserResponse (id, email, username) + token: { + access_token: string; + refresh_token: string; + expires_in: number; + }; } // ... existing code ... @@ -118,13 +123,24 @@ export async function register( console.log('[AUTH] Registration successful but complete tokens not found. This is normal if email verification is required.'); } - // Retourner au format attendu par le reste de l'application + // INT-TYPE-008: Return format aligned with backend RegisterResponse + // Backend format: { user: UserResponse, token: TokenResponse } + if (!user) { + throw new Error('Registration response missing user data'); + } + + if (!accessToken || !refreshToken || !expiresIn) { + // Registration might succeed without tokens if email verification is required + throw new Error('Registration response missing tokens. Email verification may be required.'); + } + return { - access_token: accessToken, - refresh_token: refreshToken, - expires_in: expiresIn, - token_type: accessToken ? 'Bearer' : undefined, - user: user || response.data.user || response.data.User, + user: user, + token: { + access_token: accessToken, + refresh_token: refreshToken, + expires_in: expiresIn, + }, }; } catch (error) { @@ -196,6 +212,27 @@ export async function login(data: LoginRequest): Promise { console.log('[AUTH DEBUG] Extracted from response.data.Token (camelCase fallback)'); } + // INT-TYPE-008: Handle 2FA case - if requires_2fa is true, tokens won't be present + if (response.data?.requires_2fa) { + // 2FA is required, return response without tokens + if (!user) { + // Try to extract user from response + user = response.data.user || response.data.User; + } + if (!user) { + throw new Error('Login response missing user data'); + } + return { + user: user, + token: { + access_token: '', + refresh_token: '', + expires_in: 0, + }, + requires_2fa: true, + }; + } + // Stocker les tokens dans TokenStorage (CRITIQUE pour les uploads) if (accessToken && refreshToken) { TokenStorage.setTokens(accessToken, refreshToken); @@ -218,13 +255,16 @@ export async function login(data: LoginRequest): Promise { throw new Error('Login response missing tokens'); } - // Retourner au format attendu par le reste de l'application + // INT-TYPE-008: Return format aligned with backend LoginResponse + // Backend format: { user: UserResponse, token: TokenResponse, requires_2fa?: boolean } return { - access_token: accessToken, - refresh_token: refreshToken, - expires_in: expiresIn || 3600, - token_type: 'Bearer', user: user!, + token: { + access_token: accessToken, + refresh_token: refreshToken, + expires_in: expiresIn || 3600, + }, + requires_2fa: response.data?.requires_2fa, }; } catch (error) { // Le client API transforme déjà les erreurs en ApiError via parseApiError