NeahNew/LOGIN_LOGOUT_FILES_AUDIT.md
2026-01-04 10:32:31 +01:00

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 NextAuth
  • next-auth.csrf-token - Token CSRF pour la sécurité
  • next-auth.state - État OAuth pour le flow Keycloak
  • next-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-token via signOut()

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 refreshToken du JWT
  • Comportement :
    • Appelle Keycloak /token endpoint pour rafraîchir
    • Détecte si la session Keycloak est invalide (erreur invalid_grant)
    • Retourne error: "SessionNotActive" si session Keycloak expirée

jwt callback (lignes 196-282)

  • Cookies utilisés : Lit next-auth.session-token (décrypté par NextAuth)
  • Comportement :
    • Initial login : Stocke accessToken, refreshToken, idToken dans le JWT
    • Subsequent requests : Vérifie expiration, rafraîchit si nécessaire
    • Token expired : Appelle refreshAccessToken()
    • Session invalidated : Retourne token avec error: "SessionNotActive"

session callback (lignes 283-324)

  • Cookies utilisés : Lit le JWT depuis next-auth.session-token
  • Comportement :
    • Si token.error === "SessionNotActive" → Retourne null (force logout)
    • Sinon, construit la session avec les données utilisateur

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 invalide
  • logout_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_progress ou session_invalidated

Initialisation storage (lignes 126-158)

  • Appelle /api/storage/init aprè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 : sessionStorage items (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.
document.cookie = 'logout_in_progress=true; path=/; max-age=60';

Flow de logout :

  1. Marque logout en cours (sessionStorage + cookie)
  2. Supprime cookies NextAuth (clearAuthCookies())
  3. Tente de supprimer cookies Keycloak (clearKeycloakCookies())
  4. Appelle /api/auth/end-sso-session (Admin API Keycloak)
  5. Appelle signOut() NextAuth (supprime cookie serveur)
  6. Redirige vers Keycloak logout endpoint avec id_token_hint
  7. 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() et clearKeycloakCookies()
  • 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 lit next-auth.session-token
  • Redirige vers /signin si status === "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-session pour 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 :

  1. Lit next-auth.session-token via getServerSession()
  2. Extrait idToken de la session
  3. Décode idToken pour obtenir userId
  4. Appelle adminClient.users.logout({ id: userId })
  5. 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 postMessage depuis iframes
  • Déclenche logout si message KEYCLOAK_LOGOUT reçu
  • Note : Logique similaire à layout-wrapper.tsx mais 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() avec callbackUrl
  • 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 : Lax ou None (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 : Lax ou None
  • 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 : Lax ou None
  • 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() (dans signout-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-token existe
  • 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.state vérifié et supprimé
  • next-auth.callback-url lu et utilisé

Étape 6 : JWT Callback

Fichier : app/api/auth/options.tsjwt 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.tssession 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.tsclearAuthCookies() (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.tsclearKeycloakCookies() (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=true dans 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.tsclearKeycloakCookies() 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)

  1. app/api/auth/[...nextauth]/route.ts
  2. app/api/auth/options.ts

Pages (2 fichiers)

  1. app/signin/page.tsx
  2. app/signout/page.tsx

Composants Auth (4 fichiers)

  1. components/auth/signout-handler.tsx
  2. components/auth/auth-check.tsx
  3. components/auth/signin-form.tsx (si existe)
  4. components/auth/login-card.tsx (si existe)

Composants Layout (2 fichiers)

  1. components/layout/layout-wrapper.tsx
  2. components/providers.tsx

Navigation (1 fichier)

  1. components/main-nav.tsx

Utilitaires (2 fichiers)

  1. lib/session.ts
  2. lib/keycloak.ts

API Routes (2 fichiers)

  1. app/api/auth/end-sso-session/route.ts
  2. app/api/auth/refresh-keycloak-session/route.ts

Layout Root (1 fichier)

  1. app/layout.tsx

Iframe (1 fichier)

  1. app/components/responsive-iframe.tsx

Types (1 fichier)

  1. types/next-auth.d.ts

Total : 18 fichiers principaux


🎯 FICHIERS CRITIQUES ()

Les fichiers marqués sont critiques pour le flow login/logout :

  1. app/api/auth/options.ts - Configuration NextAuth, callbacks JWT/session
  2. app/signin/page.tsx - Logique complexe de détection logout/auto-login
  3. components/auth/signout-handler.tsx - Flow complet de logout
  4. lib/session.ts - Gestion des cookies (suppression)
  5. components/layout/layout-wrapper.tsx - Écoute logout depuis iframes
  6. components/main-nav.tsx - Bouton logout
  7. app/api/auth/end-sso-session/route.ts - Termine session SSO Keycloak

📝 NOTES IMPORTANTES

  1. Cookies NextAuth : Gérés automatiquement par NextAuth, pas besoin de manipulation manuelle sauf pour suppression
  2. Cookies Keycloak : Ne peuvent pas être supprimés depuis le dashboard si domaine différent (limitation navigateur)
  3. Session SSO : Doit être terminée via Admin API Keycloak pour être complètement supprimée
  4. Auto-login : Logique complexe pour distinguer nouvel utilisateur vs session invalidée
  5. Iframe logout : Communication via postMessage pour synchroniser logout

Document créé le : $(date) Dernière mise à jour : Analyse complète du workflow login/logout avec focus sur les cookies