11 KiB
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_mepour 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 :
// É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 :
.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 :
// 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 :
// 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 :
// 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
- Opt-in par défaut : La checkbox n'est pas cochée automatiquement
- Avertissement visible : Mini-card affichée dès que cochée
- Modal éducatif : Explique les risques en détail
- Cookies httpOnly : Protégés contre le vol via JavaScript (XSS)
- Cookies secure : En production, transmis uniquement via HTTPS
- 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é
- Utilisateur se connecte avec la checkbox cochée
- Cookies Supabase définis avec
maxAge: 30 jours - Cookie
remember_me=truecréé - À chaque requête, le middleware renouvelle tous les cookies
- L'utilisateur reste connecté 30 jours (ou jusqu'à déconnexion manuelle)
Sans "Rester connecté" (défaut)
- Utilisateur se connecte sans cocher
- Cookies Supabase sans
maxAge(cookies de session) - Pas de cookie
remember_me - Les cookies expirent à la fermeture du navigateur
- L'utilisateur doit se reconnecter à chaque visite
Déconnexion manuelle
- La route
/api/auth/signoutsupprime tous les cookies (y comprisremember_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)
- Avantages : Vert (
- Animations : fadeIn + slideUp
🧪 Tests recommandés
Tests fonctionnels
- ✅ Cocher la checkbox → Mini-card s'affiche
- ✅ Décocher la checkbox → Mini-card disparaît
- ✅ Cliquer "Pourquoi ?" → Modal s'ouvre
- ✅ Cliquer "J'ai compris" → Modal se ferme
- ✅ Connexion avec checkbox cochée → Cookies 30 jours
- ✅ Connexion sans checkbox → Cookies de session
- ✅ Fermer navigateur avec "Rester connecté" → Toujours connecté
- ✅ Fermer navigateur sans "Rester connecté" → Déconnecté
- ✅ Déconnexion manuelle → Cookie
remember_mesupprimé
Tests de sécurité
- ✅ Cookies
httpOnly→ Non accessibles viadocument.cookie - ✅ Cookies
secureen prod → HTTPS uniquement - ✅ Cookies
sameSite: lax→ Protection CSRF - ✅ Durée limitée → Expiration après 30 jours
Tests UX
- ✅ Modal responsive sur mobile
- ✅ Animations fluides
- ✅ Textes clairs et informatifs
- ✅ 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 flagsecuredes cookies)- Variables Supabase existantes
Checklist de déploiement
- ✅ Vérifier que
NODE_ENV=productionen 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
- Durée configurable : Permettre à l'utilisateur de choisir (7j, 15j, 30j, 90j)
- Dashboard de sessions : Page montrant les sessions actives et permettant de les révoquer
- Notification email : Alerter l'utilisateur quand une nouvelle session longue durée est créée
- Géolocalisation : Afficher la localisation approximative de la connexion
- Historique de connexions : Log des connexions avec IP, date, navigateur
- Révocation à distance : Permettre de se déconnecter de toutes les sessions sauf la courante
📚 Références
✅ 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.