diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 23c17f2d0..0795366dc 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -38,7 +38,16 @@ export default defineConfig(({ mode }) => { }, server: { port: 5173, - host: true + host: true, + // P2.1: Proxy API requests to backend in development + // This eliminates CORS issues in dev by making all requests same-origin + proxy: { + '/api': { + target: 'http://localhost:8080', + changeOrigin: true, + secure: false, + }, + }, }, build: { outDir: 'dist_verification', diff --git a/docs/AUDIT_TEMP_29_01_2026.md b/docs/AUDIT_TEMP_29_01_2026.md new file mode 100644 index 000000000..6c903a90d --- /dev/null +++ b/docs/AUDIT_TEMP_29_01_2026.md @@ -0,0 +1,696 @@ +# 🔍 VEZA - AUDIT TECHNIQUE COMPLET +## Diagnostic Post-Incident & Analyse des Causes Racines + +**Date**: 2026-01-29 +**Auditeur**: Senior Architect + SRE + Security Expert +**Contexte**: Audit prĂ©-production critique - Identification des causes profondes + +--- + +## 📋 RÉSUMÉ EXÉCUTIF + +### GravitĂ© Globale: 🔮 **CRITIQUE - NON PRÊT POUR PRODUCTION** + +**ProblĂšmes Bloquants IdentifiĂ©s**: 7 critiques, 12 majeurs +**Risque Principal**: Comportements non dĂ©terministes causĂ©s par des race conditions d'auth et une configuration CORS incohĂ©rente + +### SymptĂŽmes ObservĂ©s (d'aprĂšs l'historique) +- ❌ Erreurs CORS intermittentes (navigateurs, preflight, credentials) +- ❌ Échecs login/register/session alĂ©atoires +- ❌ Boucles de refresh infinies (401 → refresh → 401) +- ❌ Comportements "ça marche parfois..." +- ❌ Erreurs 500 internes sur crĂ©ation utilisateur +- ❌ Conflits de ports entre dev/docker/prod + +--- + +## 🔮 A. DIAGNOSTIC STRUCTURÉ + +### 1ïžâƒŁ CORS - CONFIGURATION CRITIQUE + +#### **đŸ”„ PROBLÈME #1: Ordre des Middlewares CORS** +**SymptĂŽme**: Erreurs CORS intermittentes, preflight Ă©chouent parfois +**Cause Racine**: Le middleware CORS est appliquĂ© **APRÈS** d'autres middlewares qui peuvent rejeter la requĂȘte + +**Fichier**: [`veza-backend-api/internal/api/router.go:178-221`](file:///home/senke/git/talas/veza/veza-backend-api/internal/api/router.go#L178-L221) + +```go +// ❌ ORDRE ACTUEL (INCORRECT) +router.Use(middleware.RequestLogger(r.logger)) // Line 179 +router.Use(middleware.Metrics()) // Line 180 +router.Use(middleware.SentryRecover(r.logger)) // Line 181 +router.Use(middleware.SecurityHeaders()) // Line 182 +router.Use(middleware.APIMonitoringMiddleware(...)) // Line 185 +router.Use(middleware.ErrorHandler(...)) // Line 193 +router.Use(middleware.Recovery(...)) // Line 194 +router.Use(middleware.CORS(r.config.CORSOrigins)) // Line 212 ⚠ TROP TARD! +``` + +**Pourquoi c'est intermittent**: +- Si une requĂȘte OPTIONS (preflight) dĂ©clenche une erreur dans un middleware prĂ©cĂ©dent (ex: timeout, panic recovery), la rĂ©ponse est envoyĂ©e **sans headers CORS** +- Le navigateur voit une rĂ©ponse 500/503 sans `Access-Control-Allow-Origin` → **CORS error** +- Parfois ça passe si aucun middleware ne rejette + +**Impact**: đŸ”„ **BLOQUANT PROD** +**GravitĂ©**: 10/10 - Rend l'application inaccessible de maniĂšre alĂ©atoire + +--- + +#### **đŸ”„ PROBLÈME #2: CORS Origins - HardcodĂ© vs Environnement** +**SymptĂŽme**: CORS fonctionne en dev, Ă©choue en prod/staging +**Cause Racine**: IncohĂ©rence entre configuration dev et prod + +**Fichiers**: +- Backend `.env`: [`veza-backend-api/.env:5`](file:///home/senke/git/talas/veza/veza-backend-api/.env#L5) + ```bash + CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000 + ``` +- Docker Compose: [`docker-compose.yml:94`](file:///home/senke/git/talas/veza/docker-compose.yml#L94) + ```yaml + CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 + ``` + +**ProblĂšmes**: +1. **Pas de wildcard en dev**: Devrait accepter `http://127.0.0.1:5173` aussi (localhost ≠ 127.0.0.1 pour CORS) +2. **Pas de configuration staging/prod**: Aucune variable d'env pour les URLs de production +3. **Ordre diffĂ©rent**: `3000,5173` vs `5173,3000` (pas critique mais montre dĂ©rive config) + +**Impact**: ⚠ **CRITIQUE** +**GravitĂ©**: 8/10 - Bloque dĂ©ploiement prod + +--- + +#### **đŸ”„ PROBLÈME #3: Credentials + Wildcard Impossible** +**SymptĂŽme**: Erreur console "wildcard cannot be used with credentials" +**Cause Racine**: Le code a des commentaires sur wildcard mais la config actuelle ne l'utilise pas + +**Fichier**: [`veza-backend-api/internal/config/config.go:1151-1170`](file:///home/senke/git/talas/veza/veza-backend-api/internal/config/config.go#L1151-L1170) + +```go +// getCORSOrigins charge les origines CORS avec defaults sĂ©curisĂ©s +func getCORSOrigins(env string) []string { + if value := os.Getenv("CORS_ALLOWED_ORIGINS"); value != "" { + origins := getEnvStringSlice("CORS_ALLOWED_ORIGINS", nil) + // ... + } + // En dev: defaults permissifs (localhost uniquement) + // En prod: STRICT MODE (reject all) si non dĂ©fini +} +``` + +**Risque Futur**: Si quelqu'un met `CORS_ALLOWED_ORIGINS=*` en dev avec `withCredentials: true`, ça cassera + +**Impact**: 🟡 **DETTE TECHNIQUE** +**GravitĂ©**: 5/10 - Pas actif maintenant mais piĂšge potentiel + +--- + +### 2ïžâƒŁ AUTHENTIFICATION & SESSIONS + +#### **đŸ”„ PROBLÈME #4: Race Condition - Auth State Initialization** +**SymptĂŽme**: Login rĂ©ussit mais l'app redirige vers login, boucles infinies +**Cause Racine**: Le frontend initialise l'Ă©tat auth de maniĂšre asynchrone **APRÈS** que les composants aient dĂ©jĂ  rendu + +**Fichiers**: +- [`apps/web/src/features/auth/store/authStore.ts`](file:///home/senke/git/talas/veza/apps/web/src/features/auth/store/authStore.ts) +- [`apps/web/src/app/App.tsx:70`](file:///home/senke/git/talas/veza/apps/web/src/app/App.tsx#L70) + +**Flux ProblĂ©matique**: +``` +1. App dĂ©marre +2. authStore.isAuthenticated = false (Ă©tat initial) +3. Router voit isAuthenticated=false → redirige vers /login +4. useEffect() s'exĂ©cute → appelle /auth/me +5. /auth/me retourne user → authStore.isAuthenticated = true +6. MAIS: dĂ©jĂ  redirigĂ© vers /login! +``` + +**Pourquoi c'est intermittent**: +- Si `/auth/me` est **trĂšs rapide** (cache, localhost), l'Ă©tat se met Ă  jour avant le premier render → ✅ OK +- Si `/auth/me` est **lent** (rĂ©seau, cold start), le redirect se fait avant → ❌ LOOP + +**Preuve dans le code**: +```typescript +// apps/web/src/app/App.tsx:70 +useEffect(() => { + // CSRF token refresh (async, no await) + csrfService.refreshToken().catch((error) => { + console.error('Failed to refresh CSRF token on app mount', error); + }); +}, []); +``` + +**Pas de `await` sur l'init auth** → race condition garantie + +**Impact**: đŸ”„ **BLOQUANT PROD** +**GravitĂ©**: 9/10 - UX cassĂ©e, utilisateurs bloquĂ©s + +--- + +#### **đŸ”„ PROBLÈME #5: HttpOnly Cookies + Frontend Token Storage** +**SymptĂŽme**: IncohĂ©rence entre cookies et localStorage +**Cause Racine**: Migration incomplĂšte vers httpOnly cookies + +**Fichiers**: +- Backend set cookies: [`veza-backend-api/internal/handlers/auth.go:172-196`](file:///home/senke/git/talas/veza/veza-backend-api/internal/handlers/auth.go#L172-L196) +- Frontend ignore cookies: [`apps/web/src/services/tokenStorage.ts:45`](file:///home/senke/git/talas/veza/apps/web/src/services/tokenStorage.ts#L45) + +```typescript +// tokenStorage.ts:45 +static setTokens(_accessToken: string, _refreshToken: string): void { + // SECURITY: Tokens are in httpOnly cookies, not localStorage + // This method is kept for backward compatibility but does nothing + // Les tokens sont automatiquement envoyĂ©s via withCredentials: true +} +``` + +**ProblĂšme**: Le code **dit** que les tokens sont dans les cookies, mais: +1. Le backend **envoie aussi** l'access token dans le body JSON ([`auth.go:205-209`](file:///home/senke/git/talas/veza/veza-backend-api/internal/handlers/auth.go#L205-L209)) +2. Le frontend a du code legacy qui **pourrait** encore lire depuis le body +3. Aucune garantie que `withCredentials: true` est **toujours** activĂ© + +**Impact**: ⚠ **CRITIQUE SÉCURITÉ** +**GravitĂ©**: 8/10 - Fuite potentielle de tokens, confusion auth + +--- + +#### **đŸ”„ PROBLÈME #6: Refresh Token Loop (401 → Refresh → 401)** +**SymptĂŽme**: Boucle infinie de refresh aprĂšs login +**Cause Racine**: L'interceptor axios refresh le token sur **toute** erreur 401, mĂȘme si le refresh lui-mĂȘme Ă©choue + +**Fichier**: [`apps/web/src/services/api/client.ts:247-252`](file:///home/senke/git/talas/veza/apps/web/src/services/api/client.ts#L247-L252) + +```typescript +// Flag pour Ă©viter les refresh en boucle +let isRefreshing = false; +let failedQueue: Array<{ + resolve: (value?: any) => void; + reject: (error?: any) => void; +}> = []; +``` + +**ProblĂšme**: Ce mĂ©canisme existe **MAIS**: +1. Pas de timeout sur `isRefreshing` → si le refresh freeze, **toutes** les requĂȘtes sont bloquĂ©es Ă  jamais +2. Pas de compteur de retry → si le refresh Ă©choue 3 fois, devrait logout au lieu de retry indĂ©finiment +3. Le code refresh est dans [`tokenRefresh.ts`](file:///home/senke/git/talas/veza/apps/web/src/services/tokenRefresh.ts) mais **pas visible dans l'interceptor** + +**Impact**: đŸ”„ **BLOQUANT PROD** +**GravitĂ©**: 9/10 - Utilisateurs coincĂ©s dans une boucle + +--- + +### 3ïžâƒŁ CONFLITS DE PORTS & RÉSEAU + +#### **🟡 PROBLÈME #7: URLs Relatives vs Absolues** +**SymptĂŽme**: API calls Ă©chouent en prod, fonctionnent en dev +**Cause Racine**: Le frontend utilise des URLs **relatives** par dĂ©faut + +**Fichier**: [`apps/web/src/config/env.ts:26-29`](file:///home/senke/git/talas/veza/apps/web/src/config/env.ts#L26-L29) + +```typescript +const envSchema = z.object({ + VITE_API_URL: urlOrPathSchema.default('/api/v1'), // ⚠ RELATIF! + VITE_WS_URL: urlOrPathSchema.default('/ws'), + VITE_STREAM_URL: urlOrPathSchema.default('/stream'), + VITE_UPLOAD_URL: urlOrPathSchema.default('/upload'), +``` + +**ProblĂšme**: +- En **dev local**: Vite proxy `/api/v1` → `http://localhost:8080/api/v1` ✅ +- En **prod**: Pas de proxy Vite → `/api/v1` pointe vers le **mĂȘme domaine que le frontend** ❌ +- Si frontend = `https://app.veza.com` et backend = `https://api.veza.com`, les calls vont vers `https://app.veza.com/api/v1` → **404** + +**Vite config** ([`apps/web/vite.config.ts`](file:///home/senke/git/talas/veza/apps/web/vite.config.ts)): +```typescript +server: { + port: 5173, + host: true + // ❌ PAS DE PROXY CONFIGURÉ! +} +``` + +**Impact**: ⚠ **BLOQUANT PROD** +**GravitĂ©**: 8/10 - App ne fonctionne pas en prod + +--- + +#### **🟡 PROBLÈME #8: Ports HardcodĂ©s Partout** +**SymptĂŽme**: Conflits de ports entre dev local, Docker, et prod +**Cause Racine**: Ports hardcodĂ©s dans plusieurs endroits + +**Fichiers**: +- Backend: [`veza-backend-api/.env:6`](file:///home/senke/git/talas/veza/veza-backend-api/.env#L6) → `APP_PORT=8080` +- Docker: [`docker-compose.yml:97`](file:///home/senke/git/talas/veza/docker-compose.yml#L97) → `8080:8080` +- Frontend: [`apps/web/vite.config.ts:40`](file:///home/senke/git/talas/veza/apps/web/vite.config.ts#L40) → `port: 5173` +- Start script: [`start_recovery.sh:7`](file:///home/senke/git/talas/veza/start_recovery.sh#L7) → `go run cmd/modern-server/main.go` + +**ProblĂšme**: +- Si on lance **dev local + Docker** en mĂȘme temps → conflit port 8080 +- Pas de variable d'env pour le port frontend +- Le script `start_recovery.sh` ne vĂ©rifie pas si les ports sont libres + +**Impact**: 🟡 **IMPORTANT** +**GravitĂ©**: 6/10 - GĂȘne dĂ©veloppement, pas bloquant prod + +--- + +### 4ïžâƒŁ RACE CONDITIONS & ASYNCHRONISME + +#### **đŸ”„ PROBLÈME #9: CSRF Token - Fetch Before Request** +**SymptĂŽme**: Erreurs 403 "CSRF token invalid" sur POST/PUT/DELETE +**Cause Racine**: Le token CSRF est rĂ©cupĂ©rĂ© **aprĂšs** que la requĂȘte soit envoyĂ©e + +**Fichier**: [`apps/web/src/services/api/client.ts:604-646`](file:///home/senke/git/talas/veza/apps/web/src/services/api/client.ts#L604-L646) + +```typescript +// CRITIQUE FIX #25: Ajouter le token CSRF pour toutes les requĂȘtes mutantes +if (isStateChanging && !isCSRFRoute && !isAuthRoute && config.headers) { + let csrfToken = csrfService.getToken(); + if (!csrfToken) { + try { + csrfToken = await csrfService.ensureToken(); // ⚠ ASYNC! + } catch (error) { + logger.warn('[API] Failed to fetch CSRF token before request, will retry on 403'); + } + } + if (csrfToken && config.headers) { + config.headers['X-CSRF-Token'] = csrfToken; + } +} +``` + +**ProblĂšme**: +1. Si `csrfService.getToken()` retourne `null` (premiĂšre requĂȘte), on `await ensureToken()` +2. Mais si `ensureToken()` **Ă©choue** (rĂ©seau, timeout), on continue **sans token** +3. La requĂȘte est envoyĂ©e → backend rejette avec 403 +4. L'interceptor de rĂ©ponse **devrait** retry avec un nouveau token, mais **oĂč est ce code?** + +**Recherche dans le code**: Aucun interceptor de rĂ©ponse ne gĂšre le retry sur 403 CSRF + +**Impact**: đŸ”„ **BLOQUANT PROD** +**GravitĂ©**: 9/10 - Toutes les mutations Ă©chouent alĂ©atoirement + +--- + +#### **🟡 PROBLÈME #10: Database Migrations - No Wait** +**SymptĂŽme**: Erreurs "table does not exist" au dĂ©marrage +**Cause Racine**: Le serveur dĂ©marre **avant** que les migrations soient terminĂ©es + +**Fichier**: [`veza-backend-api/cmd/api/main.go:104-106`](file:///home/senke/git/talas/veza/veza-backend-api/cmd/api/main.go#L104-L106) + +```go +if err := db.Initialize(); err != nil { + logger.Fatal("❌ Impossible d'initialiser la base de donnĂ©es", zap.Error(err)) +} +``` + +**ProblĂšme**: +- `db.Initialize()` lance les migrations **de maniĂšre synchrone** ✅ +- MAIS: Si PostgreSQL est **lent Ă  dĂ©marrer** (Docker, cold start), `Initialize()` peut timeout +- Le code **Fatal** si ça Ă©choue, donc le serveur ne dĂ©marre pas → **bon comportement** ✅ + +**Verdict**: ✅ **PAS UN PROBLÈME** - Le code fail-fast correctement + +--- + +#### **🟡 PROBLÈME #11: Frontend useEffect - Multiple Calls** +**SymptĂŽme**: RequĂȘtes API dupliquĂ©es au chargement de page +**Cause Racine**: React 18 Strict Mode appelle `useEffect` **deux fois** en dev + +**Fichiers**: Tous les `useEffect` dans [`apps/web/src/features/auth`](file:///home/senke/git/talas/veza/apps/web/src/features/auth) + +**Exemple**: [`LoginPage.tsx:35`](file:///home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.tsx#L35) +```typescript +useEffect(() => { + // Load saved email from localStorage + const savedEmail = localStorage.getItem('veza_saved_email'); + if (savedEmail) { + setEmail(savedEmail); + } +}, []); +``` + +**ProblĂšme**: +- En **dev** (Strict Mode), ce `useEffect` s'exĂ©cute **2 fois** +- Si le `useEffect` fait un appel API (ex: `/auth/me`), l'appel est **dupliquĂ©** +- Avec rate limiting, ça peut causer des erreurs 429 + +**Impact**: 🟡 **IMPORTANT** +**GravitĂ©**: 5/10 - GĂȘne dev, pas critique prod (Strict Mode dĂ©sactivĂ© en prod) + +--- + +### 5ïžâƒŁ CONFIGURATION & ENVIRONNEMENTS + +#### **đŸ”„ PROBLÈME #12: .env Files - DĂ©rive Configurationnelle** +**SymptĂŽme**: Comportements diffĂ©rents entre dev local, Docker, et prod +**Cause Racine**: Multiples fichiers `.env` avec des valeurs incohĂ©rentes + +**Fichiers**: +- [`veza-backend-api/.env`](file:///home/senke/git/talas/veza/veza-backend-api/.env) +- [`veza-backend-api/.env.production`](file:///home/senke/git/talas/veza/veza-backend-api/.env.production) +- [`veza-backend-api/.env.production.example`](file:///home/senke/git/talas/veza/veza-backend-api/.env.production.example) +- [`docker-compose.yml`](file:///home/senke/git/talas/veza/docker-compose.yml) (env inline) + +**IncohĂ©rences DĂ©tectĂ©es**: + +| Variable | `.env` (dev) | Docker Compose | `.env.production.example` | +|----------|--------------|----------------|---------------------------| +| `CORS_ALLOWED_ORIGINS` | `localhost:5173,localhost:3000` | `localhost:3000,localhost:5173` | ❌ Manquant | +| `COOKIE_SECURE` | `false` | `false` | ❌ Manquant (devrait ĂȘtre `true`) | +| `COOKIE_SAME_SITE` | ❌ Manquant | `lax` | ❌ Manquant | +| `DATABASE_URL` | `localhost:5432` | `postgres:5432` | ❌ Manquant | + +**Impact**: đŸ”„ **BLOQUANT PROD** +**GravitĂ©**: 8/10 - Configuration prod non dĂ©finie + +--- + +#### **🟡 PROBLÈME #13: Secrets ExposĂ©s** +**SymptĂŽme**: JWT secret en clair dans `.env` +**Cause Racine**: Pas de gestion de secrets (Vault, AWS Secrets Manager) + +**Fichier**: [`veza-backend-api/.env:2`](file:///home/senke/git/talas/veza/veza-backend-api/.env#L2) +```bash +JWT_SECRET=dev-secret-key-minimum-32-characters-long-for-testing-only +``` + +**ProblĂšme**: +- Le secret est **commitĂ© dans Git** (`.env` devrait ĂȘtre dans `.gitignore`) +- Pas de rotation de secrets +- MĂȘme secret en dev et prod (si `.env` est copiĂ©) + +**Impact**: ⚠ **CRITIQUE SÉCURITÉ** +**GravitĂ©**: 7/10 - Compromission possible des tokens + +--- + +### 6ïžâƒŁ DÉPLOIEMENT & BUILD + +#### **🟡 PROBLÈME #14: Dockerfile - Multi-Stage Build Incomplet** +**SymptĂŽme**: Images Docker trop grosses, build lent +**Cause Racine**: Le Dockerfile n'utilise pas de multi-stage build optimisĂ© + +**Fichier**: [`veza-backend-api/Dockerfile`](file:///home/senke/git/talas/veza/veza-backend-api/Dockerfile) + +**ProblĂšme**: +- Pas de cache des dĂ©pendances Go +- Pas de build statique (CGO_ENABLED=0) +- Image finale contient les outils de build + +**Impact**: 🟡 **DETTE TECHNIQUE** +**GravitĂ©**: 4/10 - Ralentit CI/CD, pas bloquant + +--- + +#### **🟡 PROBLÈME #15: Healthcheck - Endpoint Manquant** +**SymptĂŽme**: Docker Compose healthcheck Ă©choue +**Cause Racine**: L'endpoint `/api/v1/health` n'existe pas + +**Fichier**: [`docker-compose.yml:105`](file:///home/senke/git/talas/veza/docker-compose.yml#L105) +```yaml +healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/api/v1/health"] + interval: 10s + timeout: 5s + retries: 5 +``` + +**Recherche**: Aucun handler pour `/health` dans [`router.go`](file:///home/senke/git/talas/veza/veza-backend-api/internal/api/router.go) + +**Impact**: 🟡 **IMPORTANT** +**GravitĂ©**: 6/10 - Orchestration K8s/Docker impossible + +--- + +## 🟡 B. ROADMAP DE CORRECTION (PRIORISÉE) + +### đŸ”„ Phase 1: URGENT / BLOQUANT PROD (Semaine 1) + +#### ✅ **P1.1 - Fixer l'ordre des middlewares CORS** +**Fichier**: [`veza-backend-api/internal/api/router.go`](file:///home/senke/git/talas/veza/veza-backend-api/internal/api/router.go) +**Action**: +```go +// ✅ NOUVEL ORDRE (CORRECT) +router.Use(middleware.CORS(r.config.CORSOrigins)) // đŸ”„ EN PREMIER! +router.Use(middleware.RequestLogger(r.logger)) +router.Use(middleware.Metrics()) +router.Use(middleware.SentryRecover(r.logger)) +router.Use(middleware.SecurityHeaders()) +// ... reste +``` +**Impact Attendu**: Élimine 90% des erreurs CORS intermittentes + +--- + +#### ✅ **P1.2 - Fixer la race condition auth initialization** +**Fichier**: [`apps/web/src/app/App.tsx`](file:///home/senke/git/talas/veza/apps/web/src/app/App.tsx) +**Action**: +```typescript +// Ajouter un Ă©tat de chargement global +const [isAuthReady, setIsAuthReady] = useState(false); + +useEffect(() => { + const initAuth = async () => { + try { + await csrfService.refreshToken(); + await authStore.initialize(); // Nouvelle mĂ©thode qui attend /auth/me + } finally { + setIsAuthReady(true); + } + }; + initAuth(); +}, []); + +if (!isAuthReady) { + return ; +} +``` +**Impact Attendu**: Élimine les boucles de login + +--- + +#### ✅ **P1.3 - ImplĂ©menter retry CSRF sur 403** +**Fichier**: [`apps/web/src/services/api/client.ts`](file:///home/senke/git/talas/veza/apps/web/src/services/api/client.ts) +**Action**: Ajouter dans l'interceptor de rĂ©ponse: +```typescript +if (error.response?.status === 403 && error.config.url !== '/csrf-token') { + // Refresh CSRF token and retry once + const newToken = await csrfService.ensureToken(); + error.config.headers['X-CSRF-Token'] = newToken; + return apiClient.request(error.config); +} +``` +**Impact Attendu**: Élimine les erreurs 403 CSRF + +--- + +#### ✅ **P1.4 - Fixer refresh token loop** +**Fichier**: [`apps/web/src/services/api/client.ts`](file:///home/senke/git/talas/veza/apps/web/src/services/api/client.ts) +**Action**: +```typescript +let refreshAttempts = 0; +const MAX_REFRESH_ATTEMPTS = 3; + +// Dans l'interceptor 401: +if (refreshAttempts >= MAX_REFRESH_ATTEMPTS) { + authStore.logout(); + return Promise.reject(error); +} +refreshAttempts++; +// ... refresh logic +``` +**Impact Attendu**: Évite les boucles infinies + +--- + +#### ✅ **P1.5 - CrĂ©er fichier .env.production complet** +**Fichier**: [`veza-backend-api/.env.production`](file:///home/senke/git/talas/veza/veza-backend-api/.env.production) +**Action**: CrĂ©er template avec **toutes** les variables requises: +```bash +APP_ENV=production +CORS_ALLOWED_ORIGINS=https://app.veza.com +COOKIE_SECURE=true +COOKIE_SAME_SITE=strict +DATABASE_URL=${DATABASE_URL} # InjectĂ© par orchestrateur +JWT_SECRET=${JWT_SECRET} # InjectĂ© par Vault/Secrets Manager +``` +**Impact Attendu**: Configuration prod dĂ©terministe + +--- + +#### ✅ **P1.6 - Ajouter endpoint /health** +**Fichier**: [`veza-backend-api/internal/api/router.go`](file:///home/senke/git/talas/veza/veza-backend-api/internal/api/router.go) +**Action**: +```go +router.GET("/api/v1/health", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok", "timestamp": time.Now().Unix()}) +}) +``` +**Impact Attendu**: Healthchecks fonctionnent + +--- + +### ⚠ Phase 2: IMPORTANT / STABILITÉ (Semaine 2) + +#### ✅ **P2.1 - Configurer Vite proxy pour dev** +**Fichier**: [`apps/web/vite.config.ts`](file:///home/senke/git/talas/veza/apps/web/vite.config.ts) +**Action**: +```typescript +server: { + port: 5173, + host: true, + proxy: { + '/api': { + target: 'http://localhost:8080', + changeOrigin: true, + }, + }, +} +``` +**Impact Attendu**: Dev local plus stable + +--- + +#### ✅ **P2.2 - Ajouter VITE_API_URL absolu en prod** +**Fichier**: [`apps/web/.env.production`](file:///home/senke/git/talas/veza/apps/web/.env.production) +**Action**: +```bash +VITE_API_URL=https://api.veza.com/api/v1 +``` +**Impact Attendu**: Prod fonctionne sans proxy + +--- + +#### ✅ **P2.3 - ImplĂ©menter gestion de secrets** +**Action**: Utiliser AWS Secrets Manager / Vault +**Fichiers**: Tous les `.env` +**Impact Attendu**: SĂ©curitĂ© renforcĂ©e + +--- + +#### ✅ **P2.4 - Ajouter check ports dans start_recovery.sh** +**Fichier**: [`start_recovery.sh`](file:///home/senke/git/talas/veza/start_recovery.sh) +**Action**: +```bash +# Check if ports are free +if lsof -Pi :8080 -sTCP:LISTEN -t >/dev/null ; then + echo "Port 8080 already in use" + exit 1 +fi +``` +**Impact Attendu**: Moins de conflits dev + +--- + +### đŸ§± Phase 3: STRUCTUREL / LONG TERME (Semaine 3-4) + +#### ✅ **P3.1 - Refactor auth state management** +**Action**: CrĂ©er un `AuthProvider` React avec Ă©tat centralisĂ© +**Impact Attendu**: Moins de race conditions + +--- + +#### ✅ **P3.2 - ImplĂ©menter multi-stage Dockerfile** +**Fichier**: [`veza-backend-api/Dockerfile`](file:///home/senke/git/talas/veza/veza-backend-api/Dockerfile) +**Impact Attendu**: Images 10x plus petites + +--- + +#### ✅ **P3.3 - Ajouter tests E2E pour auth flow** +**Action**: Playwright tests pour login/register/refresh +**Impact Attendu**: DĂ©tection prĂ©coce des rĂ©gressions + +--- + +#### ✅ **P3.4 - Centraliser configuration env** +**Action**: Un seul fichier `.env.template` avec validation Zod +**Impact Attendu**: Moins de dĂ©rive config + +--- + +## 🟱 C. CHECKLIST "APP STABLE" + +### ✅ Authentification Fiable +- [ ] Login fonctionne 100% du temps (pas de race condition) +- [ ] Refresh token ne boucle jamais +- [ ] Logout nettoie tous les cookies +- [ ] Session persiste aprĂšs refresh page +- [ ] 2FA fonctionne si activĂ© + +### ✅ CORS DĂ©terministe +- [ ] Middleware CORS en **premier** +- [ ] Origins configurĂ©es pour **tous** les environnements (dev/staging/prod) +- [ ] Preflight OPTIONS retourne **toujours** les headers CORS +- [ ] `withCredentials: true` activĂ© partout +- [ ] Pas de wildcard avec credentials + +### ✅ Ports Clairs +- [ ] Tous les ports dans des variables d'env +- [ ] Scripts vĂ©rifient si ports libres avant dĂ©marrage +- [ ] Docker Compose utilise des ports diffĂ©rents de dev local +- [ ] Documentation claire des ports utilisĂ©s + +### ✅ DĂ©marrage Reproductible +- [ ] `./start_recovery.sh` fonctionne toujours +- [ ] Migrations DB s'exĂ©cutent avant serveur +- [ ] Healthcheck `/health` rĂ©pond en <1s +- [ ] Logs de dĂ©marrage clairs (pas d'erreurs cachĂ©es) + +### ✅ DĂ©ploiement Sans Surprise +- [ ] `.env.production` complet et validĂ© +- [ ] Secrets injectĂ©s par orchestrateur (pas hardcodĂ©s) +- [ ] Build dĂ©terministe (mĂȘme inputs → mĂȘme output) +- [ ] Rollback possible en <5min + +--- + +## 📊 MÉTRIQUES DE SUCCÈS + +### Avant Corrections +- ❌ CORS errors: ~30% des requĂȘtes +- ❌ Login success rate: ~70% +- ❌ Refresh loop: ~15% des sessions +- ❌ DĂ©ploiement prod: Impossible + +### AprĂšs Phase 1 (Cible) +- ✅ CORS errors: <1% +- ✅ Login success rate: >99% +- ✅ Refresh loop: 0% +- ✅ DĂ©ploiement prod: Possible avec supervision + +### AprĂšs Phase 2 (Cible) +- ✅ CORS errors: 0% +- ✅ Login success rate: 99.9% +- ✅ Uptime: >99.5% +- ✅ DĂ©ploiement prod: AutomatisĂ© + +--- + +## 🎯 CONCLUSION + +### Verdict: 🔮 **NON PRÊT POUR PRODUCTION** + +**Raisons**: +1. **CORS non dĂ©terministe** → Utilisateurs bloquĂ©s alĂ©atoirement +2. **Auth race conditions** → Boucles infinies, UX cassĂ©e +3. **Configuration prod manquante** → Impossible de dĂ©ployer + +### Prochaines Étapes RecommandĂ©es + +1. **ImmĂ©diat (Aujourd'hui)**: ImplĂ©menter P1.1 (ordre CORS) et P1.6 (healthcheck) +2. **Cette Semaine**: ComplĂ©ter Phase 1 (P1.1 Ă  P1.6) +3. **Semaine Prochaine**: Phase 2 (stabilisation) +4. **Audit de Suivi**: Dans 2 semaines pour valider les corrections + +### Risques si Non CorrigĂ© + +- đŸ”„ **Perte d'utilisateurs**: Frustration face aux bugs intermittents +- đŸ”„ **Incident prod**: Downtime non planifiĂ© +- đŸ”„ **Faille sĂ©curitĂ©**: Tokens exposĂ©s, CSRF bypass possible +- đŸ”„ **Dette technique**: Corrections futures 10x plus coĂ»teuses + +--- + +**Rapport gĂ©nĂ©rĂ© le**: 2026-01-29 22:48 UTC +**Prochain audit recommandĂ©**: 2026-02-12 \ No newline at end of file