# Migration httpOnly Cookies - Guide Backend ## 🎯 Objectif Modifier les endpoints `/auth/login` et `/auth/refresh` pour setter des cookies httpOnly au lieu de retourner les tokens dans le body JSON. ## 📋 Fichiers Ă  Modifier ### 1. `internal/handlers/auth.go` - Handler Login **Localisation**: Ligne ~121-133 **Changements nĂ©cessaires**: ```go // AprĂšs la gĂ©nĂ©ration des tokens (ligne ~121) // Au lieu de retourner RefreshToken dans le body JSON, setter un cookie httpOnly // SECURITY: Set refresh token in httpOnly cookie refreshTokenExpires := 30 * 24 * time.Hour // 30 jours par dĂ©faut if rememberMe { refreshTokenExpires = 90 * 24 * time.Hour // 90 jours si remember me } c.SetCookie( "refresh_token", // name tokens.RefreshToken, // value int(refreshTokenExpires.Seconds()), // maxAge (en secondes) "/", // path "", // domain (vide = domaine actuel) true, // secure (HTTPS only en production) true, // httpOnly (pas accessible via JS) ) // Retourner uniquement l'access token dans le body (pas le refresh token) RespondSuccess(c, http.StatusOK, dto.LoginResponse{ User: dto.UserResponse{ ID: user.ID, Email: user.Email, Username: user.Username, }, Token: dto.TokenResponse{ AccessToken: tokens.AccessToken, // RefreshToken: tokens.RefreshToken, // ❌ Ne plus retourner dans le body ExpiresIn: int(authService.JWTService.GetConfig().AccessTokenTTL.Seconds()), }, }) ``` ### 2. `internal/handlers/auth.go` - Handler Refresh **Localisation**: Ligne ~249-256 **Changements nĂ©cessaires**: ```go // Dans la fonction Refresh, aprĂšs la gĂ©nĂ©ration des nouveaux tokens // SECURITY: Set refresh token in httpOnly cookie // Utiliser la mĂȘme durĂ©e que le refresh token original refreshTokenExpires := 30 * 24 * time.Hour // 30 jours par dĂ©faut // Note: On pourrait rĂ©cupĂ©rer la durĂ©e depuis le token original si nĂ©cessaire c.SetCookie( "refresh_token", // name tokens.RefreshToken, // value int(refreshTokenExpires.Seconds()), // maxAge "/", // path "", // domain true, // secure true, // httpOnly ) // Retourner uniquement l'access token dans le body RespondSuccess(c, http.StatusOK, dto.TokenResponse{ AccessToken: tokens.AccessToken, // RefreshToken: tokens.RefreshToken, // ❌ Ne plus retourner dans le body ExpiresIn: 900, }) ``` ### 3. `internal/handlers/auth.go` - Handler Register **Localisation**: Ligne ~136-247 **Changements nĂ©cessaires**: ```go // AprĂšs la gĂ©nĂ©ration des tokens (ligne ~231) // MĂȘme logique que pour Login refreshTokenExpires := 30 * 24 * time.Hour // 30 jours par dĂ©faut c.SetCookie( "refresh_token", tokens.RefreshToken, int(refreshTokenExpires.Seconds()), "/", "", true, // secure true, // httpOnly ) // Retourner uniquement l'access token dans le body response := dto.RegisterResponse{ User: dto.UserResponse{ ID: user.ID, Email: user.Email, Username: user.Username, }, Token: dto.TokenResponse{ AccessToken: tokens.AccessToken, // RefreshToken: tokens.RefreshToken, // ❌ Ne plus retourner ExpiresIn: tokens.ExpiresIn, }, } ``` ### 4. `internal/handlers/auth.go` - Handler Logout **Localisation**: À vĂ©rifier **Changements nĂ©cessaires**: ```go // Lors du logout, supprimer le cookie refresh_token c.SetCookie( "refresh_token", "", // valeur vide -1, // maxAge nĂ©gatif = supprimer "/", "", true, // secure true, // httpOnly ) ``` ### 5. `internal/core/auth/handler.go` - Handler Refresh (si utilisĂ©) **Localisation**: Ligne ~166-196 **Changements similaires**: ```go // AprĂšs la gĂ©nĂ©ration des nouveaux tokens (ligne ~189) c.SetCookie( "refresh_token", tokens.RefreshToken, int(30 * 24 * time.Hour.Seconds()), // 30 jours "/", "", true, // secure true, // httpOnly ) response := dto.TokenResponse{ AccessToken: tokens.AccessToken, // RefreshToken: tokens.RefreshToken, // ❌ Ne plus retourner ExpiresIn: 900, } ``` ## 🔧 Configuration Environnement ### Variables d'environnement Ă  ajouter (optionnel) ```env # Cookie settings COOKIE_SECURE=true # true en production, false en dev COOKIE_SAME_SITE=strict # strict, lax, ou none COOKIE_DOMAIN= # vide pour domaine actuel, ou spĂ©cifier le domaine ``` ### Utilisation dans le code ```go // Dans config ou env cookieSecure := os.Getenv("COOKIE_SECURE") == "true" cookieSameSite := http.SameSiteStrictMode // ou depuis env c.SetCookie( "refresh_token", tokens.RefreshToken, int(refreshTokenExpires.Seconds()), "/", cookieDomain, // depuis env ou "" cookieSecure, // depuis env true, // httpOnly toujours true ) ``` ## 🔄 CompatibilitĂ© avec Frontend Le frontend est dĂ©jĂ  prĂ©parĂ© pour cette migration : - ✅ `withCredentials: true` activĂ© dans `apiClient` - ✅ `tokenRefresh.ts` dĂ©tecte automatiquement les cookies httpOnly - ✅ Mode hybride : fonctionne avec localStorage ET cookies httpOnly ## ⚠ Notes Importantes 1. **SameSite**: Utiliser `SameSiteStrictMode` pour la sĂ©curitĂ© maximale 2. **Secure**: Toujours `true` en production (HTTPS requis) 3. **Domain**: Laisser vide pour le domaine actuel, ou spĂ©cifier si cross-domain 4. **Path**: `/` pour que le cookie soit disponible sur tout le site 5. **Tests**: Mettre Ă  jour les tests pour vĂ©rifier les cookies au lieu du body JSON ## đŸ§Ș Tests Ă  Mettre Ă  Jour 1. Tests unitaires des handlers Login/Refresh 2. Tests d'intĂ©gration pour vĂ©rifier les cookies 3. Tests E2E pour vĂ©rifier la persistance de session ## 📝 Checklist - [ ] Modifier handler Login pour setter cookie httpOnly - [ ] Modifier handler Refresh pour setter cookie httpOnly - [ ] Modifier handler Register pour setter cookie httpOnly - [ ] Modifier handler Logout pour supprimer cookie - [ ] Ajouter configuration environnement pour cookies - [ ] Mettre Ă  jour les tests unitaires - [ ] Mettre Ă  jour les tests d'intĂ©gration - [ ] Tester avec le frontend - [ ] Documenter les changements