Refactor flow 3

This commit is contained in:
alma 2026-01-04 11:06:15 +01:00
parent f27d1e0835
commit 35ce442d4f
2 changed files with 315 additions and 37 deletions

224
DEBUG_502_CALLBACK.md Normal file
View File

@ -0,0 +1,224 @@
# Debug Erreur 502 - Callback Keycloak
## 🔍 Situation Actuelle
**URL** : `https://hub.slm-lab.net/api/auth/callback/keycloak?...`
**Logs observés** :
- ✅ Profile callback : OK
- ✅ JWT callback : OK (rôles extraits depuis access token)
- ❌ Session callback : **PAS DE LOGS** (ne s'exécute pas ou échoue silencieusement)
- ❌ Erreur 502 Nginx
## 🎯 Hypothèses
### Hypothèse 1 : Session callback échoue silencieusement
Le session callback pourrait échouer avant d'atteindre les logs, causant une exception non gérée.
### Hypothèse 2 : Problème avec token.email ou token.name
Si `token.email` ou `token.name` sont `undefined` et que le code s'attend à des valeurs, cela pourrait causer une erreur.
### Hypothèse 3 : Timeout ou problème de mémoire
Le callback pourrait prendre trop de temps ou consommer trop de mémoire.
### Hypothèse 4 : Problème avec NEXTAUTH_URL ou NEXTAUTH_SECRET
Configuration manquante ou incorrecte.
## ✅ Corrections Appliquées
### 1. Logs détaillés dans session callback
- Logs au début et à la fin
- Logs de chaque étape
- Logs des valeurs de token
### 2. Try-catch complet
- Capture toutes les erreurs
- Logs détaillés de l'erreur
- Stack trace complète
### 3. Validation des champs requis
- Vérification de `token.sub` (user ID)
- Gestion des valeurs `undefined`
### 4. Events NextAuth
- `signIn` event pour tracker l'authentification
- `error` event pour capturer les erreurs NextAuth
- `signOut` event pour tracking
## 🔍 Prochaines Étapes d'Investigation
### Étape 1 : Vérifier les nouveaux logs
Après redémarrage du serveur, vous devriez voir :
```
=== SESSION CALLBACK START ===
Token error: undefined
Has accessToken: true
Has refreshToken: true
Token role: [...]
Token sub: ...
...
=== SESSION CALLBACK END ===
```
**Si vous ne voyez PAS ces logs** :
- Le session callback ne s'exécute pas du tout
- Il y a une erreur avant d'atteindre le callback
- Problème dans NextAuth lui-même
**Si vous voyez une erreur** :
- Les logs détaillés indiqueront exactement où ça échoue
### Étape 2 : Vérifier les events NextAuth
Vous devriez voir :
```
=== NEXTAUTH SIGNIN EVENT ===
User: ... ...
Account: keycloak
Profile: ...
```
**Si vous voyez `=== NEXTAUTH ERROR EVENT ===`** :
- L'erreur sera loggée avec détails
### Étape 3 : Vérifier les variables d'environnement
**Vérifier dans `.env` ou `.env.local`** :
```bash
NEXTAUTH_URL=https://hub.slm-lab.net
NEXTAUTH_SECRET=... (doit être défini)
KEYCLOAK_ISSUER=https://connect.slm-lab.net/realms/cercle
KEYCLOAK_CLIENT_ID=...
KEYCLOAK_CLIENT_SECRET=...
```
**Commandes pour vérifier** :
```bash
# Vérifier que les variables sont chargées
node -e "console.log(process.env.NEXTAUTH_URL)"
node -e "console.log(process.env.NEXTAUTH_SECRET ? 'SET' : 'MISSING')"
```
### Étape 4 : Vérifier les logs Nginx
**Si Nginx est devant Next.js**, vérifier les logs Nginx :
```bash
# Logs d'erreur Nginx
sudo tail -f /var/log/nginx/error.log
# Logs d'accès Nginx
sudo tail -f /var/log/nginx/access.log
```
**Chercher** :
- Timeout errors
- Connection refused
- Upstream errors
### Étape 5 : Vérifier les logs système
**Vérifier si Next.js crash** :
```bash
# Logs système
journalctl -u nextjs -f
# Ou si PM2
pm2 logs
# Ou si systemd
systemctl status nextjs
```
## 🛠️ Actions Immédiates
### 1. Redémarrer le serveur Next.js
```bash
# Arrêter
pm2 stop neah
# Ou
systemctl stop nextjs
# Redémarrer
pm2 start neah
# Ou
systemctl start nextjs
```
### 2. Tester à nouveau la connexion
1. Aller sur `/signin`
2. Se connecter avec Keycloak
3. Observer les logs dans le terminal
### 3. Partager les logs complets
**Ce qu'il faut partager** :
- Tous les logs depuis le début de la connexion
- Les logs jusqu'à l'erreur 502
- Les logs Nginx (si disponibles)
- Les logs système (si disponibles)
## 🔧 Solutions Possibles
### Solution 1 : Problème avec token.email ou token.name
**Si les logs montrent** :
```
Token email: undefined
Token name: undefined
```
**Correction** : Le JWT callback doit extraire email et name depuis le profil ou le token d'accès.
### Solution 2 : Problème avec NEXTAUTH_URL
**Si NEXTAUTH_URL est incorrect** :
- NextAuth ne peut pas construire les URLs de callback
- Correction : Vérifier que `NEXTAUTH_URL` correspond à l'URL publique
### Solution 3 : Problème avec NEXTAUTH_SECRET
**Si NEXTAUTH_SECRET est manquant** :
- NextAuth ne peut pas signer les JWT
- Correction : Générer un secret et l'ajouter
### Solution 4 : Timeout
**Si le callback prend trop de temps** :
- Augmenter les timeouts Nginx
- Optimiser le code du callback
## 📊 Checklist de Debugging
- [ ] Serveur Next.js redémarré
- [ ] Logs `=== SESSION CALLBACK START ===` visibles
- [ ] Logs `=== SESSION CALLBACK END ===` visibles
- [ ] Pas d'erreur dans les logs
- [ ] Variables d'environnement vérifiées
- [ ] Logs Nginx vérifiés (si applicable)
- [ ] Logs système vérifiés (si applicable)
## 🎯 Ce qu'on cherche
**Dans les prochains logs, on cherche** :
1. **Si on voit `=== SESSION CALLBACK START ===`** :
- ✅ Le callback s'exécute
- Chercher l'erreur dans les logs suivants
2. **Si on NE voit PAS `=== SESSION CALLBACK START ===`** :
- ❌ Le callback ne s'exécute pas
- Problème dans NextAuth avant le callback
- Vérifier les events NextAuth
3. **Si on voit `=== NEXTAUTH ERROR EVENT ===`** :
- ✅ NextAuth a capturé une erreur
- L'erreur sera loggée avec détails
---
**Document créé le** : $(date)
**Statut** : En attente des nouveaux logs après redémarrage

View File

@ -323,6 +323,17 @@ export const authOptions: NextAuthOptions = {
token.username = keycloakProfile.preferred_username ?? '';
token.first_name = keycloakProfile.given_name ?? '';
token.last_name = keycloakProfile.family_name ?? '';
// IMPORTANT: Set email and name for session callback
token.email = keycloakProfile.email ?? null;
token.name = keycloakProfile.name ?? keycloakProfile.preferred_username ?? null;
console.log('JWT token populated:', {
hasSub: !!token.sub,
hasEmail: !!token.email,
hasName: !!token.name,
hasUsername: !!token.username,
rolesCount: cleanRoles.length,
});
// Return immediately on initial sign-in - don't try to refresh tokens we just received
return token;
@ -392,43 +403,53 @@ export const authOptions: NextAuthOptions = {
return refreshedToken;
},
async session({ session, token }) {
console.log('=== SESSION CALLBACK ===');
console.log('Token error:', token.error);
console.log('Has accessToken:', !!token.accessToken);
console.log('Has refreshToken:', !!token.refreshToken);
console.log('Token role:', token.role);
console.log('Token sub:', token.sub);
// If session was invalidated or tokens are missing, return null to sign out
if (token.error === "SessionNotActive" ||
token.error === "NoRefreshToken" ||
!token.accessToken ||
!token.refreshToken) {
console.log("❌ Session invalidated or tokens missing, user will be signed out", {
error: token.error,
hasAccessToken: !!token.accessToken,
hasRefreshToken: !!token.refreshToken
});
// Return null to make NextAuth treat user as unauthenticated
// This will trigger automatic redirect to sign-in page
// The client-side code will detect session invalidation by checking for
// session cookie existence when status is unauthenticated
return null as any;
}
// For other errors, throw to trigger error handling
if (token.error) {
console.error("❌ Token error, throwing:", token.error);
throw new Error(token.error as string);
}
const userRoles = Array.isArray(token.role) ? token.role : [];
console.log('User roles for session:', userRoles);
try {
console.log('=== SESSION CALLBACK START ===');
console.log('Token error:', token.error);
console.log('Has accessToken:', !!token.accessToken);
console.log('Has refreshToken:', !!token.refreshToken);
console.log('Token role:', token.role);
console.log('Token sub:', token.sub);
console.log('Token email:', token.email);
console.log('Token name:', token.name);
console.log('Token username:', token.username);
// If session was invalidated or tokens are missing, return null to sign out
if (token.error === "SessionNotActive" ||
token.error === "NoRefreshToken" ||
!token.accessToken ||
!token.refreshToken) {
console.log("❌ Session invalidated or tokens missing, user will be signed out", {
error: token.error,
hasAccessToken: !!token.accessToken,
hasRefreshToken: !!token.refreshToken
});
// Return null to make NextAuth treat user as unauthenticated
// This will trigger automatic redirect to sign-in page
// The client-side code will detect session invalidation by checking for
// session cookie existence when status is unauthenticated
return null as any;
}
// For other errors, throw to trigger error handling
if (token.error) {
console.error("❌ Token error, throwing:", token.error);
throw new Error(token.error as string);
}
const userRoles = Array.isArray(token.role) ? token.role : [];
console.log('User roles for session:', userRoles);
// Validate required fields
if (!token.sub) {
console.error('❌ Missing token.sub (user ID)');
throw new Error('Missing user ID in token');
}
console.log('Creating session user object...');
session.user = {
id: (token.sub ?? '') as string,
id: token.sub as string,
email: (token.email ?? null) as string | null,
name: (token.name ?? null) as string | null,
image: null,
@ -438,15 +459,31 @@ export const authOptions: NextAuthOptions = {
role: userRoles,
nextcloudInitialized: false,
};
console.log('Setting session tokens...');
session.accessToken = token.accessToken as string | undefined;
session.idToken = token.idToken as string | undefined;
session.refreshToken = token.refreshToken as string | undefined;
console.log('✅ Session created successfully');
console.log('==============================');
console.log('Session user id:', session.user.id);
console.log('Session user email:', session.user.email);
console.log('Session user roles:', session.user.role);
console.log('=== SESSION CALLBACK END ===');
return session;
} catch (error) {
console.error('❌ Error creating session:', error);
console.error('❌❌❌ CRITICAL ERROR IN SESSION CALLBACK ❌❌❌');
console.error('Error type:', error instanceof Error ? error.constructor.name : typeof error);
console.error('Error message:', error instanceof Error ? error.message : String(error));
console.error('Error stack:', error instanceof Error ? error.stack : 'No stack trace');
console.error('Token state:', {
hasSub: !!token.sub,
hasEmail: !!token.email,
hasAccessToken: !!token.accessToken,
hasRefreshToken: !!token.refreshToken,
role: token.role,
});
// Re-throw to let NextAuth handle it
throw error;
}
}
@ -456,6 +493,23 @@ export const authOptions: NextAuthOptions = {
error: '/signin',
},
debug: process.env.NODE_ENV === 'development',
// Add error handling events
events: {
async signIn({ user, account, profile }) {
console.log('=== NEXTAUTH SIGNIN EVENT ===');
console.log('User:', user?.id, user?.email);
console.log('Account:', account?.provider);
console.log('Profile:', profile?.sub);
return true;
},
async signOut() {
console.log('=== NEXTAUTH SIGNOUT EVENT ===');
},
async error({ error }) {
console.error('=== NEXTAUTH ERROR EVENT ===');
console.error('Error:', error);
},
},
};
// JWT interface is declared in the module declaration above