import axios, { AxiosInstance } from 'axios'; import { TokenStorage } from './tokenStorage'; // T0177: Créer un client axios séparé pour le refresh pour éviter les interceptors // Lazy initialization pour faciliter les tests let refreshClient: AxiosInstance | null = null; function getRefreshClient(): AxiosInstance { if (!refreshClient) { const baseURL = (() => { const url = import.meta.env.VITE_API_URL; if (!url) { if (import.meta.env.PROD) { throw new Error('VITE_API_URL must be defined in production'); } // Fallback uniquement en développement return 'http://127.0.0.1:8080/api/v1'; } return url; })(); refreshClient = axios.create({ baseURL, timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); } return refreshClient; } /** * TokenRefresh - Service de rafraîchissement des tokens d'authentification * T0176: Service pour rafraîchir les tokens via l'endpoint /auth/refresh */ export interface RefreshTokenResponse { access_token: string; refresh_token: string; expires_in: number; } /** * Rafraîchit le token d'accès en utilisant le refresh token * T0176: Appelle l'endpoint POST /api/v1/auth/refresh et met à jour les tokens * @returns Promise qui se résout quand le token est rafraîchi * @throws Error si le refresh token n'est pas disponible ou si le refresh échoue */ export async function refreshToken(): Promise { // T0176: Récupérer le refresh token depuis le stockage const refreshToken = TokenStorage.getRefreshToken(); if (!refreshToken) { throw new Error('No refresh token available'); } try { // T0176: Appeler l'endpoint POST /auth/refresh // T0177: Utiliser refreshClient pour éviter les interceptors (qui causeraient une boucle) const client = getRefreshClient(); const response = await client.post('/auth/refresh', { refresh_token: refreshToken, }); // Le backend retourne { success: true, data: { access_token, refresh_token, expires_in } } // ou directement { access_token, refresh_token, expires_in } après unwrapping let accessToken: string | undefined; let newRefreshToken: string | undefined; // Format avec wrapper { success: true, data: {...} } if (response.data?.data?.access_token) { accessToken = response.data.data.access_token; newRefreshToken = response.data.data.refresh_token; } // Format direct { access_token, refresh_token, expires_in } else if (response.data?.access_token) { accessToken = response.data.access_token; newRefreshToken = response.data.refresh_token; } // Format avec token nested { token: { access_token, refresh_token } } else if (response.data?.token?.access_token) { accessToken = response.data.token.access_token; newRefreshToken = response.data.token.refresh_token; } if (!accessToken || !newRefreshToken) { throw new Error('Invalid refresh response format'); } // T0176: Mettre à jour les tokens stockés TokenStorage.setTokens(accessToken, newRefreshToken); } catch (error) { // T0176: Gérer les erreurs - supprimer les tokens en cas d'échec TokenStorage.clearTokens(); throw error; } }