333 lines
11 KiB
Markdown
333 lines
11 KiB
Markdown
# 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.
|