[INT-TYPE-008] Validate AuthResponse matches backend exactly
This commit is contained in:
parent
fa5c4e83ca
commit
e21ece089b
4 changed files with 79 additions and 32 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue