import { apiClient } from './api/client'; /** * CSRF Service * Gère la récupération et le stockage des tokens CSRF */ class CSRFService { private token: string | null = null; private refreshPromise: Promise | null = null; /** * Récupère un nouveau token CSRF depuis le backend */ async refreshToken(): Promise { // Éviter les appels multiples simultanés if (this.refreshPromise) { return this.refreshPromise; } this.refreshPromise = (async () => { try { const response = await apiClient.get<{ csrf_token: string }>('/csrf-token'); // apiClient unwrap déjà le format { success, data } const data = response.data; this.token = data.csrf_token; return this.token; } catch (error) { console.error('Failed to fetch CSRF token:', error); throw error; } finally { this.refreshPromise = null; } })(); return this.refreshPromise; } /** * Retourne le token CSRF actuel * Si aucun token n'est disponible, retourne null */ getToken(): string | null { return this.token; } /** * Vérifie si un token est disponible, sinon en récupère un nouveau * INT-AUTH-001: Ensure token is available before mutations */ async ensureToken(): Promise { if (this.token) { return this.token; } return this.refreshToken(); } /** * Réinitialise le token (utile après logout) */ clearToken(): void { this.token = null; this.refreshPromise = null; } /** * Alias pour compatibilité (legacy) * INT-CLEANUP-001: Kept for backward compatibility */ clearCsrfToken(): void { this.clearToken(); } /** * Alias pour compatibilité (legacy) * INT-CLEANUP-001: Kept for backward compatibility */ async refreshCsrfToken(): Promise { return this.refreshToken(); } /** * Retourne les headers CSRF pour les requêtes fetch */ getCsrfHeaders(): Record { const token = this.getToken(); if (!token) { return {}; } return { 'X-CSRF-Token': token, }; } } // Instance singleton export const csrfService = new CSRFService();