23 KiB
Audit Complet des Fichiers Login/Logout - Analyse des Cookies
📋 Vue d'ensemble
Ce document liste TOUS les fichiers de code impliqués dans le processus de login et logout du dashboard Next.js avec NextAuth et Keycloak, avec une analyse approfondie de la gestion des cookies.
🔐 FICHIERS CORE - Configuration NextAuth
1. app/api/auth/[...nextauth]/route.ts
Rôle : Route handler NextAuth pour tous les endpoints d'authentification Cookies gérés :
next-auth.session-token(ou variantes sécurisées) - Cookie principal de session NextAuthnext-auth.csrf-token- Token CSRF pour la sécuriténext-auth.state- État OAuth pour le flow Keycloaknext-auth.callback-url- URL de callback après authentification
Fonctions :
- Gère
GET/POST /api/auth/signin→ Redirige vers Keycloak - Gère
GET/POST /api/auth/signout→ Déconnecte et nettoie les cookies - Gère
GET /api/auth/session→ Lit le cookie de session - Gère
GET /api/auth/callback/keycloak→ Reçoit le code OAuth de Keycloak - Gère
GET /api/auth/csrf→ Génère le token CSRF - Gère
GET /api/auth/providers→ Liste les providers disponibles
Cookies créés/supprimés :
- Login : Crée
next-auth.session-token(HttpOnly, Secure, SameSite=Lax) - Logout : Supprime
next-auth.session-tokenviasignOut()
2. app/api/auth/options.ts ⭐ FICHIER CRITIQUE
Rôle : Configuration principale de NextAuth avec Keycloak Cookies gérés :
- Tous les cookies NextAuth (via configuration implicite)
- Les tokens Keycloak sont stockés dans le JWT (pas de cookies séparés)
Fonctions clés :
refreshAccessToken(token) (lignes 83-139)
- Cookies utilisés : Aucun directement, mais utilise
refreshTokendu JWT - Comportement :
- Appelle Keycloak
/tokenendpoint pour rafraîchir - Détecte si la session Keycloak est invalide (erreur
invalid_grant) - Retourne
error: "SessionNotActive"si session Keycloak expirée
- Appelle Keycloak
jwt callback (lignes 196-282)
- Cookies utilisés : Lit
next-auth.session-token(décrypté par NextAuth) - Comportement :
- Initial login : Stocke
accessToken,refreshToken,idTokendans le JWT - Subsequent requests : Vérifie expiration, rafraîchit si nécessaire
- Token expired : Appelle
refreshAccessToken() - Session invalidated : Retourne token avec
error: "SessionNotActive"
- Initial login : Stocke
session callback (lignes 283-324)
- Cookies utilisés : Lit le JWT depuis
next-auth.session-token - Comportement :
- Si
token.error === "SessionNotActive"→ Retournenull(force logout) - Sinon, construit la session avec les données utilisateur
- Si
Configuration cookies :
session: {
strategy: "jwt",
maxAge: 4 * 60 * 60, // 4 heures
}
// Les cookies sont gérés automatiquement par NextAuth
// Pas de configuration explicite des cookies dans ce fichier
Paramètres OAuth :
authorization: {
params: {
scope: "openid profile email roles",
prompt: "login" // Force le prompt de login même si SSO existe
}
}
🚪 FICHIERS PAGES - Interface Utilisateur
3. app/signin/page.tsx ⭐ FICHIER CRITIQUE
Rôle : Page de connexion avec logique complexe de détection de logout Cookies analysés :
next-auth.session-token(ou variantes) - Vérifie si cookie existe mais invalidelogout_in_progress- Cookie temporaire (60s) pour marquer logout en cours- Cookies Keycloak (via
document.cookie)
Fonctions clés :
Détection de logout/session invalide (lignes 17-67)
// Vérifie les cookies NextAuth
const hasInvalidSessionCookie = document.cookie
.split(';')
.some(c => c.trim().startsWith('next-auth.session-token=') ||
c.trim().startsWith('__Secure-next-auth.session-token=') ||
c.trim().startsWith('__Host-next-auth.session-token='));
// Si cookie existe mais status = unauthenticated → Session invalidée
if (status === 'unauthenticated' && hasInvalidSessionCookie) {
sessionStorage.setItem('session_invalidated', 'true');
// Empêche auto-login
}
Auto-login (lignes 69-124)
- Condition : Seulement si PAS de cookie de session existant
- Comportement : Appelle
signIn("keycloak")après 1 seconde - Protection : Ne s'exécute pas si
logout_in_progressousession_invalidated
Initialisation storage (lignes 126-158)
- Appelle
/api/storage/initaprès authentification réussie - Force reload pour mettre à jour la session
Cookies créés/supprimés :
- Aucun cookie créé directement (NextAuth gère ça)
- Supprime :
sessionStorageitems (just_logged_out,session_invalidated)
4. app/signout/page.tsx
Rôle : Page de déconnexion (simple wrapper)
Cookies : Aucune manipulation directe, délègue à SignOutHandler
🔧 FICHIERS COMPOSANTS - Logique Métier
5. components/auth/signout-handler.tsx ⭐ FICHIER CRITIQUE
Rôle : Gère la déconnexion complète (NextAuth + Keycloak) Cookies manipulés :
Cookies NextAuth (ligne 23)
clearAuthCookies(); // Supprime next-auth.session-token
Cookies Keycloak (ligne 25)
clearKeycloakCookies(); // Tente de supprimer KEYCLOAK_SESSION, etc.
Cookie de flag (ligne 16)
document.cookie = 'logout_in_progress=true; path=/; max-age=60';
Flow de logout :
- Marque logout en cours (
sessionStorage+ cookie) - Supprime cookies NextAuth (
clearAuthCookies()) - Tente de supprimer cookies Keycloak (
clearKeycloakCookies()) - Appelle
/api/auth/end-sso-session(Admin API Keycloak) - Appelle
signOut()NextAuth (supprime cookie serveur) - Redirige vers Keycloak logout endpoint avec
id_token_hint - Keycloak redirige vers
/signin?logout=true
Cookies supprimés :
next-auth.session-token(et variantes)KEYCLOAK_SESSION,KEYCLOAK_IDENTITY,AUTH_SESSION_ID(si même domaine)logout_in_progress(expire après 60s)
6. components/main-nav.tsx (lignes 364-446)
Rôle : Bouton de déconnexion dans la navigation
Cookies : Même logique que signout-handler.tsx
- Appelle
clearAuthCookies()etclearKeycloakCookies() - Crée cookie
logout_in_progress - Même flow que
SignOutHandler
7. components/layout/layout-wrapper.tsx ⭐ FICHIER CRITIQUE
Rôle : Écoute les messages de logout depuis les iframes Cookies manipulés :
- Même pattern que
signout-handler.tsx - Gère les logout déclenchés par les iframes via
postMessage
Fonction clé (lignes 23-112) :
const handleMessage = async (event: MessageEvent) => {
if (event.data.type === 'KEYCLOAK_LOGOUT' || event.data.type === 'LOGOUT') {
// Même flow que signout-handler.tsx
clearAuthCookies();
clearKeycloakCookies();
// ... logout complet
}
};
Cookies : Identique à signout-handler.tsx
8. components/auth/auth-check.tsx
Rôle : Guard d'authentification côté client Cookies : Aucune manipulation directe
- Utilise
useSession()qui litnext-auth.session-token - Redirige vers
/signinsistatus === "unauthenticated"
9. components/providers.tsx
Rôle : Wrapper SessionProvider pour NextAuth
Cookies : Aucune manipulation, fournit le contexte de session
🛠️ FICHIERS UTILITAIRES - Gestion Sessions/Cookies
10. lib/session.ts ⭐ FICHIER CRITIQUE
Rôle : Utilitaires pour gérer les cookies et sessions
clearAuthCookies() (lignes 93-108)
Cookies supprimés :
// Supprime SEULEMENT les cookies de session, PAS les cookies OAuth
if (cookieName.startsWith('next-auth.session-token') ||
cookieName.startsWith('__Secure-next-auth.session-token') ||
cookieName.startsWith('__Host-next-auth.session-token')) {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}
Important : Ne supprime PAS :
next-auth.csrf-token(nécessaire pour OAuth)next-auth.state(nécessaire pour OAuth flow)next-auth.callback-url
clearKeycloakCookies() (lignes 115-154)
Cookies Keycloak tentés de supprimer :
const keycloakCookieNames = [
'KEYCLOAK_SESSION',
'KEYCLOAK_SESSION_LEGACY',
'KEYCLOAK_IDENTITY',
'KEYCLOAK_IDENTITY_LEGACY',
'AUTH_SESSION_ID',
'KC_RESTART',
'KC_RESTART_LEGACY'
];
Limitation : Ces cookies sont sur le domaine Keycloak, donc ne peuvent pas être supprimés depuis le domaine du dashboard (même origine). Cette fonction tente plusieurs combinaisons domain/path mais échouera si Keycloak est sur un domaine différent.
invalidateServiceTokens() (lignes 53-91)
Cookies : Aucun, invalide les tokens de services externes (RocketChat, Leantime)
11. lib/keycloak.ts
Rôle : Client Admin Keycloak pour gestion serveur Cookies : Aucune manipulation directe
- Utilisé par
/api/auth/end-sso-sessionpour terminer la session SSO
🌐 FICHIERS API - Endpoints Serveur
12. app/api/auth/end-sso-session/route.ts ⭐ FICHIER CRITIQUE
Rôle : Termine la session SSO Keycloak via Admin API Cookies : Aucune manipulation directe
- Utilise Keycloak Admin API pour logout utilisateur
- Important : Termine la session realm-wide, pas seulement client
Flow :
- Lit
next-auth.session-tokenviagetServerSession() - Extrait
idTokende la session - Décode
idTokenpour obteniruserId - Appelle
adminClient.users.logout({ id: userId }) - Keycloak supprime toutes les sessions de l'utilisateur
Impact cookies :
- Keycloak supprime ses cookies côté serveur
- Les cookies Keycloak deviennent invalides (mais restent dans le navigateur jusqu'à expiration)
13. app/api/auth/refresh-keycloak-session/route.ts
Rôle : Rafraîchit la session Keycloak (si existe)
Cookies : Lit next-auth.session-token via getServerSession()
📄 FICHIERS LAYOUT - Structure Application
14. app/layout.tsx
Rôle : Layout racine avec vérification de session serveur
Cookies : Lit next-auth.session-token via getServerSession(authOptions)
- Passe
isAuthenticatedàLayoutWrapper - Détermine si c'est la page signin
15. app/components/responsive-iframe.tsx (lignes 109-153)
Rôle : Composant iframe avec écoute de messages logout Cookies : Aucune manipulation directe
- Écoute
postMessagedepuis iframes - Déclenche logout si message
KEYCLOAK_LOGOUTreçu - Note : Logique similaire à
layout-wrapper.tsxmais dans le composant iframe
📝 FICHIERS TYPES - Définitions TypeScript
16. types/next-auth.d.ts
Rôle : Extensions TypeScript pour NextAuth Cookies : Aucune manipulation, définit les types de session/JWT
🔍 ANALYSE DÉTAILLÉE DES COOKIES
Cookies NextAuth
1. next-auth.session-token (ou variantes sécurisées)
- Domaine : Domaine du dashboard
- Path :
/ - HttpOnly :
true(sécurité) - Secure :
true(si HTTPS) - SameSite :
Lax(par défaut) - Contenu : JWT encrypté contenant :
accessToken(Keycloak)refreshToken(Keycloak)idToken(Keycloak)- Données utilisateur (id, email, roles, etc.)
- Durée : 4 heures (configuré dans
options.ts) - Créé : Lors de
signIn()réussi - Supprimé : Lors de
signOut()ou expiration - Variantes :
__Secure-next-auth.session-token(si HTTPS)__Host-next-auth.session-token(si domaine racine)
2. next-auth.csrf-token
- Domaine : Domaine du dashboard
- Path :
/ - HttpOnly :
true - Secure :
true(si HTTPS) - SameSite :
Lax - Contenu : Token CSRF pour protection OAuth
- Durée : Session (supprimé à la fermeture du navigateur)
- Créé : Lors de la première requête OAuth
- Supprimé : À la fermeture du navigateur
- Important : N'EST PAS supprimé par
clearAuthCookies()(nécessaire pour OAuth)
3. next-auth.state
- Domaine : Domaine du dashboard
- Path :
/ - HttpOnly :
true - Secure :
true(si HTTPS) - SameSite :
Lax - Contenu : État OAuth pour validation du callback
- Durée : Court (pendant le flow OAuth)
- Créé : Lors de
signIn()(début flow OAuth) - Supprimé : Après validation du callback OAuth
- Important : N'EST PAS supprimé par
clearAuthCookies()(nécessaire pour OAuth)
4. next-auth.callback-url
- Domaine : Domaine du dashboard
- Path :
/ - HttpOnly :
true - Secure :
true(si HTTPS) - SameSite :
Lax - Contenu : URL de redirection après authentification
- Durée : Court (pendant le flow OAuth)
- Créé : Lors de
signIn()aveccallbackUrl - Supprimé : Après redirection
Cookies Keycloak
1. KEYCLOAK_SESSION
- Domaine : Domaine Keycloak (peut être différent du dashboard)
- Path :
/ou/realms/{realm} - HttpOnly :
true - Secure :
true(si HTTPS) - SameSite :
LaxouNone(pour cross-site) - Contenu : Identifiant de session SSO Keycloak
- Durée : Configuré dans Keycloak (typiquement 30 min - quelques heures)
- Créé : Lors de l'authentification Keycloak
- Supprimé : Lors de logout Keycloak ou expiration
- Problème : Ne peut pas être supprimé depuis le dashboard si domaine différent
2. KEYCLOAK_IDENTITY
- Domaine : Domaine Keycloak
- Path :
/ou/realms/{realm} - HttpOnly :
true - Secure :
true - SameSite :
LaxouNone - Contenu : Identité utilisateur Keycloak
- Durée : Même que
KEYCLOAK_SESSION - Créé : Lors de l'authentification Keycloak
- Supprimé : Lors de logout Keycloak ou expiration
3. AUTH_SESSION_ID
- Domaine : Domaine Keycloak
- Path :
/ou/realms/{realm} - HttpOnly :
true - Secure :
true - SameSite :
LaxouNone - Contenu : ID de session d'authentification
- Durée : Court (pendant le flow d'authentification)
- Créé : Lors du début du flow d'authentification
- Supprimé : Après authentification réussie ou échec
Cookies Custom
1. logout_in_progress
- Domaine : Domaine du dashboard
- Path :
/ - HttpOnly :
false(accessible via JavaScript) - Secure :
false - SameSite : Non défini
- Contenu :
"true" - Durée : 60 secondes (
max-age=60) - Créé : Lors de
signOut()(danssignout-handler.tsx,main-nav.tsx,layout-wrapper.tsx) - Supprimé : Expire après 60s ou manuellement
- Usage : Empêche l'auto-login après logout
🔄 FLOW COMPLET DE LOGIN
Étape 1 : Utilisateur accède à /signin
Fichier : app/signin/page.tsx
Cookies :
- Vérifie si
next-auth.session-tokenexiste - Si existe mais
status === "unauthenticated"→ Session invalidée - Si n'existe pas → Nouvel utilisateur, déclenche auto-login
Étape 2 : Auto-login déclenché
Fichier : app/signin/page.tsx (ligne 118)
Action : signIn("keycloak", { callbackUrl: "/" })
Cookies créés :
next-auth.csrf-token(par NextAuth)next-auth.state(par NextAuth)next-auth.callback-url(par NextAuth)
Étape 3 : Redirection vers Keycloak
Fichier : app/api/auth/[...nextauth]/route.ts → NextAuth interne
URL : ${KEYCLOAK_ISSUER}/protocol/openid-connect/auth?...&prompt=login
Cookies Keycloak créés :
AUTH_SESSION_ID(par Keycloak)
Étape 4 : Authentification Keycloak
Fichier : Keycloak serveur Cookies Keycloak créés :
KEYCLOAK_SESSION(session SSO)KEYCLOAK_IDENTITY(identité utilisateur)
Étape 5 : Callback OAuth
Fichier : app/api/auth/callback/keycloak (géré par NextAuth)
Cookies :
next-auth.statevérifié et suppriménext-auth.callback-urllu et utilisé
Étape 6 : JWT Callback
Fichier : app/api/auth/options.ts → jwt callback (ligne 196)
Cookies :
- Lit
next-auth.session-token(décrypté) - Stocke tokens Keycloak dans le JWT
- Crée
next-auth.session-token(nouveau JWT avec tokens)
Étape 7 : Session Callback
Fichier : app/api/auth/options.ts → session callback (ligne 283)
Cookies : Lit next-auth.session-token pour construire la session
Étape 8 : Redirection vers /
Fichier : app/signin/page.tsx (ligne 72)
Cookies : next-auth.session-token maintenant présent
Étape 9 : Initialisation Storage
Fichier : app/signin/page.tsx (lignes 126-158)
Action : Appelle /api/storage/init
Cookies : Utilise next-auth.session-token (via getServerSession())
🔄 FLOW COMPLET DE LOGOUT
Étape 1 : Utilisateur clique "Déconnexion"
Fichiers :
components/main-nav.tsx(ligne 364)- OU
components/auth/signout-handler.tsx(ligne 11) - OU
components/layout/layout-wrapper.tsx(ligne 32) si message iframe
Cookies créés :
logout_in_progress=true; path=/; max-age=60(ligne 16/369/38)sessionStorage.setItem('just_logged_out', 'true')(ligne 14/367/37)
Étape 2 : Suppression cookies NextAuth
Fichier : lib/session.ts → clearAuthCookies() (ligne 93)
Cookies supprimés :
next-auth.session-token(et variantes)- PAS
next-auth.csrf-token(nécessaire pour OAuth) - PAS
next-auth.state(nécessaire pour OAuth)
Étape 3 : Tentative suppression cookies Keycloak
Fichier : lib/session.ts → clearKeycloakCookies() (ligne 115)
Cookies tentés de supprimer :
KEYCLOAK_SESSION,KEYCLOAK_IDENTITY, etc.- Limitation : Échoue si Keycloak sur domaine différent
Étape 4 : Fin de session SSO via Admin API
Fichier : app/api/auth/end-sso-session/route.ts (ligne 15)
Action : adminClient.users.logout({ id: userId })
Cookies :
- Keycloak supprime toutes les sessions côté serveur
- Les cookies Keycloak deviennent invalides (mais restent dans le navigateur)
Étape 5 : SignOut NextAuth
Fichier : components/auth/signout-handler.tsx (ligne 52)
Action : signOut({ callbackUrl: "/signin?logout=true", redirect: false })
Cookies supprimés :
next-auth.session-token(supprimé côté serveur)
Étape 6 : Redirection vers Keycloak Logout
Fichier : components/auth/signout-handler.tsx (ligne 58)
URL : ${KEYCLOAK_ISSUER}/protocol/openid-connect/logout?...&id_token_hint=...&kc_action=LOGOUT
Cookies Keycloak :
- Keycloak supprime ses cookies (si même domaine ou cross-domain configuré)
Étape 7 : Redirection vers /signin?logout=true
Fichier : Keycloak → app/signin/page.tsx
Cookies :
next-auth.session-token: SuppriméKEYCLOAK_SESSION: Peut encore exister (si domaine différent)logout_in_progress: Existe encore (60s)
Étape 8 : Détection logout dans signin
Fichier : app/signin/page.tsx (lignes 17-67)
Cookies vérifiés :
logout_in_progress(ligne 19)next-auth.session-token(ligne 25-29)sessionStorage.getItem('just_logged_out')(ligne 20)
Comportement :
- Si
logout=truedans URL → Affiche message "Vous avez été déconnecté" - Si cookie session existe mais invalide → Empêche auto-login
- Si pas de cookie session → Auto-login après 1s (nouvel utilisateur)
⚠️ PROBLÈMES IDENTIFIÉS
Problème 1 : Cookies Keycloak non supprimables
Fichier : lib/session.ts → clearKeycloakCookies()
Cause : Cookies Keycloak sur domaine différent
Impact : Les cookies Keycloak persistent après logout dashboard
Solution actuelle : Appel à Keycloak logout endpoint avec id_token_hint
Problème 2 : Session SSO Keycloak peut persister
Fichier : app/api/auth/options.ts (ligne 154)
Cause : prompt=login force le prompt, mais si SSO session existe, Keycloak peut auto-authentifier
Impact : Utilisateur peut être reconnecté automatiquement sans credentials
Solution actuelle : prompt=login + appel Admin API pour terminer session SSO
Problème 3 : Détection session invalide complexe
Fichier : app/signin/page.tsx (lignes 17-67)
Cause : Logique complexe pour détecter si session invalidée vs nouvel utilisateur
Impact : Auto-login peut se déclencher incorrectement
Solution actuelle : Vérification multiple (cookies, sessionStorage, URL params)
Problème 4 : Race condition logout/login
Fichier : app/signin/page.tsx (lignes 69-124)
Cause : Auto-login avec délai de 1s peut se déclencher pendant logout
Impact : Utilisateur peut être reconnecté immédiatement après logout
Solution actuelle : Flags logout_in_progress et session_invalidated
📊 RÉSUMÉ DES FICHIERS PAR CATÉGORIE
Configuration Core (2 fichiers)
app/api/auth/[...nextauth]/route.tsapp/api/auth/options.ts⭐
Pages (2 fichiers)
app/signin/page.tsx⭐app/signout/page.tsx
Composants Auth (4 fichiers)
components/auth/signout-handler.tsx⭐components/auth/auth-check.tsxcomponents/auth/signin-form.tsx(si existe)components/auth/login-card.tsx(si existe)
Composants Layout (2 fichiers)
components/layout/layout-wrapper.tsx⭐components/providers.tsx
Navigation (1 fichier)
components/main-nav.tsx⭐
Utilitaires (2 fichiers)
lib/session.ts⭐lib/keycloak.ts
API Routes (2 fichiers)
app/api/auth/end-sso-session/route.ts⭐app/api/auth/refresh-keycloak-session/route.ts
Layout Root (1 fichier)
app/layout.tsx
Iframe (1 fichier)
app/components/responsive-iframe.tsx
Types (1 fichier)
types/next-auth.d.ts
Total : 18 fichiers principaux
🎯 FICHIERS CRITIQUES (⭐)
Les fichiers marqués ⭐ sont critiques pour le flow login/logout :
app/api/auth/options.ts- Configuration NextAuth, callbacks JWT/sessionapp/signin/page.tsx- Logique complexe de détection logout/auto-logincomponents/auth/signout-handler.tsx- Flow complet de logoutlib/session.ts- Gestion des cookies (suppression)components/layout/layout-wrapper.tsx- Écoute logout depuis iframescomponents/main-nav.tsx- Bouton logoutapp/api/auth/end-sso-session/route.ts- Termine session SSO Keycloak
📝 NOTES IMPORTANTES
- Cookies NextAuth : Gérés automatiquement par NextAuth, pas besoin de manipulation manuelle sauf pour suppression
- Cookies Keycloak : Ne peuvent pas être supprimés depuis le dashboard si domaine différent (limitation navigateur)
- Session SSO : Doit être terminée via Admin API Keycloak pour être complètement supprimée
- Auto-login : Logique complexe pour distinguer nouvel utilisateur vs session invalidée
- Iframe logout : Communication via
postMessagepour synchroniser logout
Document créé le : $(date) Dernière mise à jour : Analyse complète du workflow login/logout avec focus sur les cookies