647 lines
23 KiB
Markdown
647 lines
23 KiB
Markdown
# 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** :
|
|
```typescript
|
|
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** :
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
// 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)
|
|
```typescript
|
|
clearAuthCookies(); // Supprime next-auth.session-token
|
|
```
|
|
|
|
#### Cookies Keycloak (ligne 25)
|
|
```typescript
|
|
clearKeycloakCookies(); // Tente de supprimer KEYCLOAK_SESSION, etc.
|
|
```
|
|
|
|
#### Cookie de flag (ligne 16)
|
|
```typescript
|
|
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) :
|
|
```typescript
|
|
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** :
|
|
```typescript
|
|
// 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** :
|
|
```typescript
|
|
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.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=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.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)
|
|
1. `app/api/auth/[...nextauth]/route.ts`
|
|
2. `app/api/auth/options.ts` ⭐
|
|
|
|
### Pages (2 fichiers)
|
|
3. `app/signin/page.tsx` ⭐
|
|
4. `app/signout/page.tsx`
|
|
|
|
### Composants Auth (4 fichiers)
|
|
5. `components/auth/signout-handler.tsx` ⭐
|
|
6. `components/auth/auth-check.tsx`
|
|
7. `components/auth/signin-form.tsx` (si existe)
|
|
8. `components/auth/login-card.tsx` (si existe)
|
|
|
|
### Composants Layout (2 fichiers)
|
|
9. `components/layout/layout-wrapper.tsx` ⭐
|
|
10. `components/providers.tsx`
|
|
|
|
### Navigation (1 fichier)
|
|
11. `components/main-nav.tsx` ⭐
|
|
|
|
### Utilitaires (2 fichiers)
|
|
12. `lib/session.ts` ⭐
|
|
13. `lib/keycloak.ts`
|
|
|
|
### API Routes (2 fichiers)
|
|
14. `app/api/auth/end-sso-session/route.ts` ⭐
|
|
15. `app/api/auth/refresh-keycloak-session/route.ts`
|
|
|
|
### Layout Root (1 fichier)
|
|
16. `app/layout.tsx`
|
|
|
|
### Iframe (1 fichier)
|
|
17. `app/components/responsive-iframe.tsx`
|
|
|
|
### Types (1 fichier)
|
|
18. `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
|
|
|