2025-12-03 21:56:50 +00:00
|
|
|
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) {
|
2025-12-17 13:07:35 +00:00
|
|
|
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;
|
|
|
|
|
})();
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
refreshClient = axios.create({
|
2025-12-17 13:07:35 +00:00
|
|
|
baseURL,
|
2025-12-03 21:56:50 +00:00
|
|
|
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<void> {
|
|
|
|
|
// 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();
|
2025-12-22 21:00:50 +00:00
|
|
|
const response = await client.post<any>('/auth/refresh', {
|
2025-12-03 21:56:50 +00:00
|
|
|
refresh_token: refreshToken,
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-22 21:00:50 +00:00
|
|
|
// 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');
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
// T0176: Mettre à jour les tokens stockés
|
2025-12-22 21:00:50 +00:00
|
|
|
TokenStorage.setTokens(accessToken, newRefreshToken);
|
2025-12-03 21:56:50 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
// T0176: Gérer les erreurs - supprimer les tokens en cas d'échec
|
|
|
|
|
TokenStorage.clearTokens();
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|