[INT-TYPE-008] Validate AuthResponse matches backend exactly

This commit is contained in:
senke 2025-12-25 22:39:41 +01:00
parent fa5c4e83ca
commit e21ece089b
4 changed files with 79 additions and 32 deletions

View file

@ -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
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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<LoginResponse> {
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<LoginResponse> {
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