Refactor flow 3
This commit is contained in:
parent
f27d1e0835
commit
35ce442d4f
224
DEBUG_502_CALLBACK.md
Normal file
224
DEBUG_502_CALLBACK.md
Normal 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
|
||||||
|
|
||||||
@ -323,6 +323,17 @@ export const authOptions: NextAuthOptions = {
|
|||||||
token.username = keycloakProfile.preferred_username ?? '';
|
token.username = keycloakProfile.preferred_username ?? '';
|
||||||
token.first_name = keycloakProfile.given_name ?? '';
|
token.first_name = keycloakProfile.given_name ?? '';
|
||||||
token.last_name = keycloakProfile.family_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 immediately on initial sign-in - don't try to refresh tokens we just received
|
||||||
return token;
|
return token;
|
||||||
@ -392,43 +403,53 @@ export const authOptions: NextAuthOptions = {
|
|||||||
return refreshedToken;
|
return refreshedToken;
|
||||||
},
|
},
|
||||||
async session({ session, token }) {
|
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 {
|
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 = {
|
session.user = {
|
||||||
id: (token.sub ?? '') as string,
|
id: token.sub as string,
|
||||||
email: (token.email ?? null) as string | null,
|
email: (token.email ?? null) as string | null,
|
||||||
name: (token.name ?? null) as string | null,
|
name: (token.name ?? null) as string | null,
|
||||||
image: null,
|
image: null,
|
||||||
@ -438,15 +459,31 @@ export const authOptions: NextAuthOptions = {
|
|||||||
role: userRoles,
|
role: userRoles,
|
||||||
nextcloudInitialized: false,
|
nextcloudInitialized: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('Setting session tokens...');
|
||||||
session.accessToken = token.accessToken as string | undefined;
|
session.accessToken = token.accessToken as string | undefined;
|
||||||
session.idToken = token.idToken as string | undefined;
|
session.idToken = token.idToken as string | undefined;
|
||||||
session.refreshToken = token.refreshToken as string | undefined;
|
session.refreshToken = token.refreshToken as string | undefined;
|
||||||
|
|
||||||
console.log('✅ Session created successfully');
|
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;
|
return session;
|
||||||
} catch (error) {
|
} 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;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,6 +493,23 @@ export const authOptions: NextAuthOptions = {
|
|||||||
error: '/signin',
|
error: '/signin',
|
||||||
},
|
},
|
||||||
debug: process.env.NODE_ENV === 'development',
|
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
|
// JWT interface is declared in the module declaration above
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user