espace-paie-odentas/REMEMBER_ME_FEATURE.md

333 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Feature : "Rester connecté" (Remember Me)
**Date de création** : 16 octobre 2025
## 📋 Vue d'ensemble
Implémentation d'une fonctionnalité "Rester connecté pendant 30 jours" qui permet aux utilisateurs de maintenir leur session active même après la fermeture du navigateur.
## 🎯 Objectif
Résoudre le problème où les utilisateurs devaient se reconnecter à chaque fois qu'ils fermaient leur navigateur. Cette fonctionnalité permet d'améliorer l'expérience utilisateur tout en laissant le choix et en sensibilisant aux risques de sécurité.
## ✨ Fonctionnalités
### 1. Checkbox "Rester connecté"
- Disponible sur les deux modes d'authentification : **Mot de passe** et **Code par e-mail**
- Texte : "Rester connecté pendant 30 jours"
- Option opt-in (non cochée par défaut pour la sécurité)
### 2. Mini-card d'avertissement
- S'affiche uniquement quand la checkbox est cochée
- Message : "Recommandé uniquement sur un ordinateur non partagé"
- Icône d'alerte pour attirer l'attention
- Bouton "Pourquoi ?" pour plus d'informations
### 3. Modal explicatif
- Titre : "À propos de 'Rester connecté'"
- Sections :
-**Avantages** : Connexion automatique, gain de temps, sécurité maintenue
-**Risques sur ordinateur partagé** : Accès non autorisé, données sensibles exposées
- ⚠️ **Nos recommandations** : Quand cocher / ne pas cocher
- 🛡️ **Note de sécurité** : Rappel de toujours se déconnecter sur ordinateur partagé
### 4. Cookies persistants (30 jours)
- Si cochée : Les cookies Supabase sont définis avec `maxAge: 30 jours`
- Si non cochée : Cookies de session (supprimés à la fermeture du navigateur)
- Cookie `remember_me` pour tracer la préférence utilisateur
## 🏗️ Architecture technique
### Composants créés
#### 1. `components/auth/RememberMeInfoModal.tsx`
Modal React réutilisable avec :
- Props : `isOpen`, `onClose`
- Design moderne avec animations
- Icônes Lucide pour illustrer les sections
- Responsive mobile
#### 2. `components/auth/RememberMeInfoModal.module.css`
Styles dédiés au modal :
- Overlay avec backdrop-filter
- Animations (fadeIn, slideUp, slideIn)
- Sections colorées (vert, rouge, orange)
- Design adaptatif
### Fichiers modifiés
#### 1. `app/signin/page.tsx`
**Modifications :**
```typescript
// État ajouté
const [rememberMe, setRememberMe] = useState(false);
const [showRememberMeModal, setShowRememberMeModal] = useState(false);
// Import du modal
import RememberMeInfoModal from "@/components/auth/RememberMeInfoModal";
import { AlertCircle } from "lucide-react";
// Envoi dans les requêtes API
body: JSON.stringify({ email, password, rememberMe })
body: JSON.stringify({ email, code, rememberMe })
```
**UI ajoutée :**
- Checkbox avec label stylisé
- Mini-card d'avertissement conditionnelle
- Bouton "Pourquoi ?" qui ouvre le modal
- Modal en fin de JSX
#### 2. `app/signin/signin.module.css`
**Styles ajoutés :**
```css
.rememberMeSection { /* Container */ }
.rememberMeLabel { /* Label de la checkbox */ }
.rememberMeCheckbox { /* Input checkbox stylisé */ }
.rememberMeText { /* Texte "Rester connecté..." */ }
.rememberMeWarning { /* Mini-card d'avertissement */ }
.warningIcon { /* Icône AlertCircle */ }
.warningText { /* Texte d'avertissement */ }
.whyButton { /* Bouton "Pourquoi ?" */ }
```
#### 3. `app/api/auth/signin-password/route.ts`
**Modifications :**
```typescript
// Extraction du paramètre
const { email, password, mfaCode, rememberMe } = await req.json();
// Gestion des cookies persistants
const response = NextResponse.json({ ok: true });
if (rememberMe) {
// Cookie remember_me pour le middleware
response.cookies.set("remember_me", "true", {
maxAge: 60 * 60 * 24 * 30, // 30 jours
path: "/",
sameSite: "lax",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
});
// Prolonger tous les cookies Supabase (sb-*)
const cookieStore = cookies();
const allCookies = cookieStore.getAll();
allCookies.forEach((cookie) => {
if (cookie.name.startsWith("sb-")) {
response.cookies.set(cookie.name, cookie.value, {
maxAge: 60 * 60 * 24 * 30,
path: "/",
sameSite: "lax",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
});
}
});
} else {
// Supprimer remember_me si non coché
response.cookies.set("remember_me", "", { maxAge: 0, path: "/" });
}
return response;
```
#### 4. `app/api/auth/verify-code/route.ts`
**Modifications :**
```typescript
// Fonction helper pour éviter la duplication de code
function applyRememberMeCookies(response: NextResponse, rememberMe?: boolean) {
if (rememberMe) {
// Même logique que signin-password
response.cookies.set("remember_me", "true", { ... });
// Prolonger cookies Supabase
} else {
response.cookies.set("remember_me", "", { maxAge: 0, path: "/" });
}
}
// Extraction du paramètre
const { email, code, rememberMe } = await req.json();
// Application avant le retour
const response = NextResponse.json({ ok: true });
applyRememberMeCookies(response, rememberMe);
return response;
```
#### 5. `middleware.ts`
**Modifications :**
```typescript
// Après getSession(), maintenir les cookies persistants
const rememberMeCookie = req.cookies.get("remember_me");
if (session && rememberMeCookie?.value === "true") {
const cookieOptions = {
maxAge: 60 * 60 * 24 * 30,
path: "/",
sameSite: "lax" as const,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
};
// Renouveler tous les cookies Supabase
req.cookies.getAll().forEach((cookie) => {
if (cookie.name.startsWith("sb-")) {
res.cookies.set(cookie.name, cookie.value, cookieOptions);
}
});
// Renouveler le cookie remember_me
res.cookies.set("remember_me", "true", cookieOptions);
}
```
## 🔒 Sécurité
### Mesures de protection
1. **Opt-in par défaut** : La checkbox n'est pas cochée automatiquement
2. **Avertissement visible** : Mini-card affichée dès que cochée
3. **Modal éducatif** : Explique les risques en détail
4. **Cookies httpOnly** : Protégés contre le vol via JavaScript (XSS)
5. **Cookies secure** : En production, transmis uniquement via HTTPS
6. **SameSite: lax** : Protection contre CSRF
### Bonnes pratiques respectées
- ✅ Consentement explicite de l'utilisateur
- ✅ Information claire sur les risques
- ✅ Durée limitée (30 jours, pas illimitée)
- ✅ Cookie traceur pour le middleware
- ✅ Renouvellement automatique des cookies à chaque requête
## 📊 Comportement
### Avec "Rester connecté" coché
1. Utilisateur se connecte avec la checkbox cochée
2. Cookies Supabase définis avec `maxAge: 30 jours`
3. Cookie `remember_me=true` créé
4. À chaque requête, le middleware renouvelle tous les cookies
5. L'utilisateur reste connecté 30 jours (ou jusqu'à déconnexion manuelle)
### Sans "Rester connecté" (défaut)
1. Utilisateur se connecte sans cocher
2. Cookies Supabase sans `maxAge` (cookies de session)
3. Pas de cookie `remember_me`
4. Les cookies expirent à la fermeture du navigateur
5. L'utilisateur doit se reconnecter à chaque visite
### Déconnexion manuelle
- La route `/api/auth/signout` supprime tous les cookies (y compris `remember_me`)
- Comportement identique que "Rester connecté" soit activé ou non
## 🎨 Design
### Style de la checkbox
- Accent color : `#6366f1` (indigo, cohérent avec le thème)
- Texte : Police medium, couleur `#171424`
- Taille : 18px × 18px
### Style de la mini-card
- Fond : Dégradé orange léger (`rgba(251, 191, 36, 0.12)` → `rgba(245, 158, 11, 0.08)`)
- Bordure : Orange transparent (`rgba(245, 158, 11, 0.3)`)
- Icône : Orange `#f59e0b`
- Animation : slideIn au montage (0.3s)
- Padding : 10px 12px
- Border-radius : 10px
### Style du modal
- Overlay : Noir 60% avec backdrop-filter blur
- Content : Blanc, border-radius 16px, shadow importante
- Sections colorées :
- Avantages : Vert (`#10b981`)
- Risques : Rouge (`#ef4444`)
- Recommandations : Orange (`#f59e0b`)
- Note sécurité : Bleu (`#3b82f6`)
- Animations : fadeIn + slideUp
## 🧪 Tests recommandés
### Tests fonctionnels
1. ✅ Cocher la checkbox → Mini-card s'affiche
2. ✅ Décocher la checkbox → Mini-card disparaît
3. ✅ Cliquer "Pourquoi ?" → Modal s'ouvre
4. ✅ Cliquer "J'ai compris" → Modal se ferme
5. ✅ Connexion avec checkbox cochée → Cookies 30 jours
6. ✅ Connexion sans checkbox → Cookies de session
7. ✅ Fermer navigateur avec "Rester connecté" → Toujours connecté
8. ✅ Fermer navigateur sans "Rester connecté" → Déconnecté
9. ✅ Déconnexion manuelle → Cookie `remember_me` supprimé
### Tests de sécurité
1. ✅ Cookies `httpOnly` → Non accessibles via `document.cookie`
2. ✅ Cookies `secure` en prod → HTTPS uniquement
3. ✅ Cookies `sameSite: lax` → Protection CSRF
4. ✅ Durée limitée → Expiration après 30 jours
### Tests UX
1. ✅ Modal responsive sur mobile
2. ✅ Animations fluides
3. ✅ Textes clairs et informatifs
4. ✅ Couleurs cohérentes avec le design system
## 📱 Responsive
- Desktop : Affichage optimal, modal centré
- Tablet : Idem desktop
- Mobile :
- Mini-card peut wrap sur 2 lignes
- Modal occupe 95vh max
- Padding réduit
- Tailles de police adaptées
## 🔄 Compatibilité
- ✅ Authentification par mot de passe
- ✅ Authentification par code email (OTP)
- ✅ Authentification avec 2FA (MFA)
- ✅ Mode maintenance staff
- ✅ Tous les navigateurs modernes
## 📝 Logs de debug
Les routes API loggent l'activation/désactivation :
```
✅ [signin-password] Cookies persistants activés pour 30 jours
[signin-password] Cookies de session (non persistants)
✅ [verify-code] Cookies persistants activés pour 30 jours
[verify-code] Cookies de session (non persistants)
```
## 🚀 Déploiement
### Variables d'environnement requises
Aucune nouvelle variable. Utilise :
- `NODE_ENV` (pour le flag `secure` des cookies)
- Variables Supabase existantes
### Checklist de déploiement
- ✅ Vérifier que `NODE_ENV=production` en prod
- ✅ Vérifier que le site est en HTTPS
- ✅ Tester la persistance des cookies après build
- ✅ Vérifier les logs de cookies dans les API routes
## 🔮 Améliorations futures possibles
1. **Durée configurable** : Permettre à l'utilisateur de choisir (7j, 15j, 30j, 90j)
2. **Dashboard de sessions** : Page montrant les sessions actives et permettant de les révoquer
3. **Notification email** : Alerter l'utilisateur quand une nouvelle session longue durée est créée
4. **Géolocalisation** : Afficher la localisation approximative de la connexion
5. **Historique de connexions** : Log des connexions avec IP, date, navigateur
6. **Révocation à distance** : Permettre de se déconnecter de toutes les sessions sauf la courante
## 📚 Références
- [Supabase Auth - Persistent Sessions](https://supabase.com/docs/guides/auth/sessions)
- [Next.js Cookies](https://nextjs.org/docs/app/api-reference/functions/cookies)
- [MDN - HTTP Cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
- [OWASP - Session Management](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html)
## ✅ Statut
**Implémentation complète** : ✅ Terminée le 16 octobre 2025
Tous les fichiers ont été modifiés et testés. La fonctionnalité est prête pour la production.