keycloak improve with build 6
This commit is contained in:
parent
f0c109ed8e
commit
61bc7d6809
259
IFRAME_LOGOUT_AUTO_LOGIN_ANALYSIS.md
Normal file
259
IFRAME_LOGOUT_AUTO_LOGIN_ANALYSIS.md
Normal file
@ -0,0 +1,259 @@
|
||||
# Iframe Logout Auto-Login Issue Analysis
|
||||
|
||||
## Problem
|
||||
|
||||
When you log out from an iframe application, you are automatically logged back into the dashboard without being prompted for credentials.
|
||||
|
||||
## Flow Trace
|
||||
|
||||
### Scenario: User Logs Out from Iframe Application
|
||||
|
||||
#### Step 1: Iframe Application Logout
|
||||
```
|
||||
Location: Iframe application (e.g., /parole, /gite, etc.)
|
||||
Action: User clicks logout in iframe
|
||||
|
||||
What happens:
|
||||
- Iframe app may call Keycloak logout endpoint directly
|
||||
- OR: Iframe app sends postMessage to parent: { type: 'KEYCLOAK_LOGOUT' }
|
||||
- OR: Iframe app clears its own session cookies
|
||||
```
|
||||
|
||||
#### Step 2A: If Iframe Sends PostMessage (Expected Flow)
|
||||
```
|
||||
Location: components/layout/layout-wrapper.tsx (line 26-106)
|
||||
OR: app/components/responsive-iframe.tsx (line 110-153)
|
||||
|
||||
Action: Dashboard receives logout message
|
||||
|
||||
What happens:
|
||||
1. Sets sessionStorage.setItem('just_logged_out', 'true')
|
||||
2. Sets document.cookie = 'logout_in_progress=true; path=/; max-age=60'
|
||||
3. Calls /api/auth/end-sso-session (Admin API)
|
||||
4. Calls signOut() from NextAuth
|
||||
5. Redirects to Keycloak logout endpoint
|
||||
6. Keycloak redirects back to /signin?logout=true
|
||||
```
|
||||
|
||||
#### Step 2B: If Iframe Calls Keycloak Logout Directly (Actual Flow - Problem)
|
||||
```
|
||||
Location: Iframe application
|
||||
|
||||
Action: Iframe calls Keycloak logout endpoint directly
|
||||
|
||||
What happens:
|
||||
1. Iframe redirects to: ${KEYCLOAK_ISSUER}/protocol/openid-connect/logout
|
||||
2. Keycloak clears session cookies
|
||||
3. Keycloak may redirect iframe back to its own logout page
|
||||
4. Dashboard doesn't know about this logout
|
||||
5. Dashboard still has NextAuth session (valid for 30 days)
|
||||
```
|
||||
|
||||
#### Step 3: Dashboard Detects Session Invalidation
|
||||
```
|
||||
Location: app/api/auth/options.ts (refreshAccessToken function)
|
||||
|
||||
When: NextAuth tries to refresh the access token
|
||||
|
||||
What happens:
|
||||
1. Dashboard calls Keycloak token refresh endpoint
|
||||
2. Keycloak returns: { error: 'invalid_grant', error_description: 'Session not active' }
|
||||
3. refreshAccessToken detects this error (line 100-108)
|
||||
4. Returns token with error: "SessionNotActive"
|
||||
5. JWT callback clears tokens (line 248-256)
|
||||
6. Session callback returns null (line 272-276)
|
||||
7. NextAuth treats user as unauthenticated
|
||||
8. Status becomes "unauthenticated"
|
||||
```
|
||||
|
||||
#### Step 4: Sign-In Page Auto-Login (THE PROBLEM)
|
||||
```
|
||||
Location: app/signin/page.tsx (line 47-79)
|
||||
|
||||
When: User is redirected to /signin (or status becomes "unauthenticated")
|
||||
|
||||
What happens:
|
||||
1. Component mounts
|
||||
2. First useEffect (line 16-45) checks for logout flag
|
||||
- If logout=true in URL, sets isLogoutRedirect.current = true
|
||||
- Removes 'just_logged_out' from sessionStorage
|
||||
3. Second useEffect (line 47-79) checks authentication status
|
||||
- If status === "authenticated" → redirects to home ✅
|
||||
- If status === "unauthenticated" → triggers auto-login ❌
|
||||
|
||||
THE PROBLEM:
|
||||
- When iframe logs out directly (not via postMessage), dashboard doesn't set logout flags
|
||||
- Status becomes "unauthenticated" (because Keycloak session was cleared)
|
||||
- Sign-in page sees status === "unauthenticated"
|
||||
- Auto-login logic triggers after 1 second (line 69)
|
||||
- signIn("keycloak") is called
|
||||
- Keycloak still has SSO session cookie (if it wasn't fully cleared)
|
||||
- User is auto-authenticated without credentials ❌
|
||||
```
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
### Problem 1: Missing Logout Flags
|
||||
|
||||
**When iframe logs out directly (not via postMessage):**
|
||||
- Dashboard doesn't know about the logout
|
||||
- `just_logged_out` is NOT set in sessionStorage
|
||||
- `logout_in_progress` cookie is NOT set
|
||||
- Sign-in page doesn't know this is a logout scenario
|
||||
|
||||
**Result**: Sign-in page treats it as a normal "unauthenticated" state and triggers auto-login.
|
||||
|
||||
### Problem 2: Auto-Login Logic Timing
|
||||
|
||||
**Sign-in page auto-login logic** (`app/signin/page.tsx:66-78`):
|
||||
```typescript
|
||||
if (status === "unauthenticated") {
|
||||
hasAttemptedLogin.current = true;
|
||||
const timer = setTimeout(() => {
|
||||
if (!isLogoutRedirect.current) {
|
||||
signIn("keycloak", { callbackUrl: "/" });
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
```
|
||||
|
||||
**The Issue**:
|
||||
- `isLogoutRedirect.current` is set in the first useEffect (line 16-45)
|
||||
- But it only checks for `logout=true` in URL or `just_logged_out` in sessionStorage
|
||||
- If iframe logs out directly, neither of these is set
|
||||
- After 1 second, auto-login triggers
|
||||
- `isLogoutRedirect.current` is still `false` (because logout flags weren't set)
|
||||
- `signIn("keycloak")` is called
|
||||
- User is auto-authenticated
|
||||
|
||||
### Problem 3: SSO Session Cookie Persistence
|
||||
|
||||
**Even if logout flags are set correctly:**
|
||||
- Keycloak SSO session cookie (`KEYCLOAK_SESSION`) may still exist
|
||||
- When `signIn("keycloak")` is called, Keycloak checks for SSO session cookie
|
||||
- If cookie exists, Keycloak auto-authenticates without credentials
|
||||
- This happens even with `prompt=login` parameter (if SSO session is still valid)
|
||||
|
||||
## Why This Happens
|
||||
|
||||
### Flow 1: Iframe Logs Out via PostMessage (Works Correctly)
|
||||
```
|
||||
1. Iframe sends postMessage → Dashboard receives it
|
||||
2. Dashboard sets logout flags ✅
|
||||
3. Dashboard calls logout endpoints ✅
|
||||
4. Redirects to /signin?logout=true ✅
|
||||
5. Sign-in page sees logout=true ✅
|
||||
6. Auto-login is prevented ✅
|
||||
7. User must click "Se connecter" manually ✅
|
||||
```
|
||||
|
||||
### Flow 2: Iframe Logs Out Directly (THE PROBLEM)
|
||||
```
|
||||
1. Iframe calls Keycloak logout directly
|
||||
2. Keycloak clears session cookies
|
||||
3. Dashboard doesn't know about logout ❌
|
||||
4. NextAuth tries to refresh token
|
||||
5. Keycloak returns "Session not active"
|
||||
6. NextAuth marks user as unauthenticated
|
||||
7. User is redirected to /signin (no logout=true) ❌
|
||||
8. Sign-in page sees status="unauthenticated" ❌
|
||||
9. Auto-login triggers after 1 second ❌
|
||||
10. Keycloak still has SSO session cookie ❌
|
||||
11. User is auto-authenticated ❌
|
||||
```
|
||||
|
||||
## The Real Issue
|
||||
|
||||
**The sign-in page auto-login logic is too aggressive:**
|
||||
|
||||
1. It triggers auto-login for ANY "unauthenticated" state
|
||||
2. It doesn't distinguish between:
|
||||
- User never logged in (should auto-login) ✅
|
||||
- User logged out (should NOT auto-login) ❌
|
||||
- Session expired (should NOT auto-login) ❌
|
||||
- Keycloak session invalidated (should NOT auto-login) ❌
|
||||
|
||||
3. The logout detection only works if:
|
||||
- `logout=true` is in URL (from Keycloak redirect)
|
||||
- `just_logged_out` is in sessionStorage (from dashboard logout)
|
||||
- But NOT if iframe logs out directly
|
||||
|
||||
## Solution Requirements
|
||||
|
||||
To fix this issue, you need to:
|
||||
|
||||
1. **Detect Keycloak Session Invalidation**:
|
||||
- When NextAuth detects "SessionNotActive" error
|
||||
- Set a flag to prevent auto-login
|
||||
- Mark this as a logout scenario, not a new login
|
||||
|
||||
2. **Improve Logout Detection**:
|
||||
- Check for Keycloak session cookie existence
|
||||
- If session was invalidated (not just expired), prevent auto-login
|
||||
- Store logout reason in sessionStorage
|
||||
|
||||
3. **Modify Auto-Login Logic**:
|
||||
- Only auto-login if:
|
||||
- User is truly unauthenticated (never logged in)
|
||||
- AND no logout flags are set
|
||||
- AND no session invalidation detected
|
||||
- Don't auto-login if:
|
||||
- Logout flags are set
|
||||
- Session was invalidated
|
||||
- User came from a logout flow
|
||||
|
||||
4. **Handle Iframe Direct Logout**:
|
||||
- Detect when Keycloak session is invalidated
|
||||
- Set logout flags automatically
|
||||
- Prevent auto-login
|
||||
|
||||
## Current Code Issues
|
||||
|
||||
### Issue 1: Auto-Login Logic (`app/signin/page.tsx:66-78`)
|
||||
```typescript
|
||||
if (status === "unauthenticated") {
|
||||
// This triggers for ANY unauthenticated state
|
||||
// Doesn't check if session was invalidated
|
||||
signIn("keycloak", { callbackUrl: "/" });
|
||||
}
|
||||
```
|
||||
|
||||
### Issue 2: Logout Detection (`app/signin/page.tsx:16-45`)
|
||||
```typescript
|
||||
// Only checks for explicit logout flags
|
||||
// Doesn't detect session invalidation
|
||||
const logoutParam = searchParams.get('logout');
|
||||
const fromLogout = sessionStorage.getItem('just_logged_out');
|
||||
```
|
||||
|
||||
### Issue 3: Session Invalidation Detection (`app/api/auth/options.ts:248-256`)
|
||||
```typescript
|
||||
// Detects session invalidation
|
||||
// But doesn't set logout flags
|
||||
// Sign-in page doesn't know session was invalidated
|
||||
if (refreshedToken.error === "SessionNotActive") {
|
||||
return {
|
||||
...refreshedToken,
|
||||
accessToken: undefined,
|
||||
// Should set a flag here to prevent auto-login
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Why you're auto-logged in after iframe logout:**
|
||||
|
||||
1. Iframe logs out directly (not via postMessage)
|
||||
2. Keycloak session is cleared
|
||||
3. Dashboard detects session invalidation
|
||||
4. User becomes "unauthenticated"
|
||||
5. Sign-in page auto-login logic triggers (after 1 second)
|
||||
6. Keycloak still has SSO session cookie
|
||||
7. User is auto-authenticated without credentials
|
||||
|
||||
**The fix requires:**
|
||||
- Detecting session invalidation and setting logout flags
|
||||
- Preventing auto-login when session was invalidated
|
||||
- Only auto-login for truly new users (never logged in)
|
||||
|
||||
232
INACTIVITY_AND_LOGOUT_ANALYSIS.md
Normal file
232
INACTIVITY_AND_LOGOUT_ANALYSIS.md
Normal file
@ -0,0 +1,232 @@
|
||||
# Inactivity Timeout and Logout Analysis
|
||||
|
||||
## Issue 1: Dashboard Should Disconnect After 30 Minutes of Inactivity
|
||||
|
||||
### Current State
|
||||
|
||||
**Session Configuration** (`app/api/auth/options.ts:190`):
|
||||
```typescript
|
||||
session: {
|
||||
strategy: "jwt",
|
||||
maxAge: 30 * 24 * 60 * 60, // 30 days
|
||||
}
|
||||
```
|
||||
|
||||
**SessionProvider Configuration** (`components/providers.tsx`):
|
||||
```typescript
|
||||
<SessionProvider>
|
||||
{children}
|
||||
</SessionProvider>
|
||||
```
|
||||
|
||||
### Problem Analysis
|
||||
|
||||
1. **No Inactivity Detection**:
|
||||
- NextAuth session is set to 30 days maximum
|
||||
- No client-side inactivity timeout logic exists
|
||||
- No activity tracking (mouse movements, clicks, keyboard input)
|
||||
- SessionProvider doesn't have `refetchInterval` configured
|
||||
|
||||
2. **How NextAuth Sessions Work**:
|
||||
- NextAuth sessions are JWT-based (stateless)
|
||||
- Session validity is checked on each request to `/api/auth/session`
|
||||
- No automatic expiration based on inactivity
|
||||
- Session expires only when `maxAge` is reached (30 days in your case)
|
||||
|
||||
3. **What's Missing**:
|
||||
- Client-side activity monitoring
|
||||
- Automatic session invalidation after inactivity period
|
||||
- Session refresh based on activity (not just time)
|
||||
|
||||
### Root Cause
|
||||
|
||||
**NextAuth doesn't track user activity** - it only tracks session age. The session will remain valid for 30 days regardless of whether the user is active or not.
|
||||
|
||||
### Solution Requirements
|
||||
|
||||
To implement 30-minute inactivity timeout, you need:
|
||||
|
||||
1. **Client-Side Activity Tracking**:
|
||||
- Monitor user activity (mouse, keyboard, clicks)
|
||||
- Track last activity timestamp
|
||||
- Store in `sessionStorage` or `localStorage`
|
||||
|
||||
2. **Session Invalidation Logic**:
|
||||
- Check inactivity period on each page interaction
|
||||
- Call `signOut()` if inactivity exceeds 30 minutes
|
||||
- Clear NextAuth session and Keycloak session
|
||||
|
||||
3. **Activity Reset on User Actions**:
|
||||
- Reset inactivity timer on any user interaction
|
||||
- Update last activity timestamp
|
||||
|
||||
4. **SessionProvider Configuration**:
|
||||
- Optionally configure `refetchInterval` to check session periodically
|
||||
- But this won't help with inactivity - it only refreshes the session
|
||||
|
||||
### Implementation Approach
|
||||
|
||||
The inactivity timeout must be implemented **client-side** because:
|
||||
- NextAuth sessions are stateless (JWT)
|
||||
- Server doesn't know about user activity
|
||||
- Activity tracking requires browser events
|
||||
|
||||
**Recommended Implementation**:
|
||||
1. Create an `InactivityHandler` component
|
||||
2. Monitor user activity events (mousemove, keydown, click, scroll)
|
||||
3. Store last activity time in `sessionStorage`
|
||||
4. Check inactivity every minute (or on page focus)
|
||||
5. If inactivity > 30 minutes, trigger logout
|
||||
|
||||
---
|
||||
|
||||
## Issue 2: Applications Outside Dashboard Still Connected After Logout
|
||||
|
||||
### Current Implementation
|
||||
|
||||
**Logout Flow** (`components/main-nav.tsx`, `components/auth/signout-handler.tsx`):
|
||||
1. Clear NextAuth cookies
|
||||
2. Clear Keycloak cookies (client-side attempt)
|
||||
3. Call `/api/auth/end-sso-session` (NEW)
|
||||
- Uses Keycloak Admin API: `adminClient.users.logout({ id: userId })`
|
||||
4. Sign out from NextAuth
|
||||
5. Redirect to Keycloak logout endpoint with `id_token_hint`
|
||||
|
||||
### Problem Analysis
|
||||
|
||||
**Why Applications Are Still Connected:**
|
||||
|
||||
1. **Keycloak Admin API `users.logout()` Behavior**:
|
||||
- The method `adminClient.users.logout({ id: userId })` **logs out the user from all client sessions**
|
||||
- However, it may **NOT clear the SSO session cookie** (`KEYCLOAK_SESSION`)
|
||||
- The SSO session cookie is what allows applications to auto-authenticate
|
||||
|
||||
2. **SSO Session vs Client Sessions**:
|
||||
- **Client Sessions**: Per OAuth client (dashboard, app1, app2, etc.)
|
||||
- **SSO Session**: Realm-wide, shared across all clients
|
||||
- `users.logout()` clears client sessions but may leave SSO session active
|
||||
- Applications check for SSO session cookie, not client sessions
|
||||
|
||||
3. **Cookie Domain/Path Issues**:
|
||||
- Keycloak cookies are set on Keycloak's domain
|
||||
- Client-side `clearKeycloakCookies()` may not work if:
|
||||
- Cookies are `HttpOnly` (can't be cleared from JavaScript)
|
||||
- Cookies are on different domain (cross-domain restrictions)
|
||||
- Cookies have different path/domain settings
|
||||
|
||||
4. **Logout Endpoint Behavior**:
|
||||
- Keycloak logout endpoint (`/protocol/openid-connect/logout`) with `id_token_hint`:
|
||||
- Clears the **client session** for that specific OAuth client
|
||||
- May clear SSO session **only if it's the last client session**
|
||||
- If other applications have active sessions, SSO session persists
|
||||
|
||||
### Root Cause
|
||||
|
||||
**The SSO session cookie persists** because:
|
||||
1. `users.logout()` Admin API method clears client sessions but may not clear SSO session cookie
|
||||
2. Keycloak logout endpoint only clears SSO session if it's the last client session
|
||||
3. If other applications have active sessions, the SSO session remains valid
|
||||
4. Applications check for SSO session cookie, not client sessions
|
||||
|
||||
### Why This Happens
|
||||
|
||||
**Keycloak's SSO Design**:
|
||||
- SSO session is designed to persist across client logouts
|
||||
- This allows users to stay logged in across multiple applications
|
||||
- Logging out from one application shouldn't log out from all applications
|
||||
- This is **by design** for SSO functionality
|
||||
|
||||
**However**, when you want **global logout**, you need to:
|
||||
1. Clear the SSO session cookie explicitly
|
||||
2. Or ensure all client sessions are logged out first
|
||||
3. Or use Keycloak's Single Logout (SLO) feature
|
||||
|
||||
### Solution Requirements
|
||||
|
||||
To ensure applications are logged out:
|
||||
|
||||
1. **Keycloak Configuration** (Server-Side):
|
||||
- Enable **Front-Channel Logout** for all clients
|
||||
- Configure **Back-Channel Logout URLs** for each client
|
||||
- This allows Keycloak to notify all applications when logout occurs
|
||||
|
||||
2. **Admin API Limitations**:
|
||||
- `users.logout()` may not clear SSO session cookie
|
||||
- Need to use Keycloak's logout endpoint with proper parameters
|
||||
- Or use Keycloak Admin API to end SSO session directly (if available)
|
||||
|
||||
3. **Alternative Approach**:
|
||||
- Use Keycloak's **Single Logout (SLO)** feature
|
||||
- Configure all clients to participate in SLO
|
||||
- When one client logs out, all clients are notified
|
||||
|
||||
### What's Actually Happening
|
||||
|
||||
When you call `/api/auth/end-sso-session`:
|
||||
1. ✅ Admin API `users.logout()` is called
|
||||
2. ✅ All client sessions are logged out
|
||||
3. ❌ SSO session cookie may still exist
|
||||
4. ❌ Applications check SSO session cookie → still authenticated
|
||||
|
||||
When you redirect to Keycloak logout endpoint:
|
||||
1. ✅ Dashboard client session is cleared
|
||||
2. ✅ If it's the last client session, SSO session is cleared
|
||||
3. ❌ If other applications have active sessions, SSO session persists
|
||||
4. ❌ Applications can still authenticate using SSO session cookie
|
||||
|
||||
### Verification Steps
|
||||
|
||||
To verify why applications are still connected:
|
||||
|
||||
1. **Check if Admin API call succeeds**:
|
||||
- Look for console logs: "Successfully ended SSO session for user: {userId}"
|
||||
- Check for errors in `/api/auth/end-sso-session` endpoint
|
||||
|
||||
2. **Check Keycloak session cookies**:
|
||||
- After logout, check browser cookies for:
|
||||
- `KEYCLOAK_SESSION`
|
||||
- `KEYCLOAK_SESSION_LEGACY`
|
||||
- `KEYCLOAK_IDENTITY`
|
||||
- If these cookies still exist, SSO session is still active
|
||||
|
||||
3. **Check if other applications have active sessions**:
|
||||
- If other applications are open in other tabs/windows
|
||||
- They may have active client sessions
|
||||
- This prevents SSO session from being cleared
|
||||
|
||||
4. **Check Keycloak Admin Console**:
|
||||
- Navigate to: Users → [User] → Sessions
|
||||
- Check if sessions are actually cleared
|
||||
- Verify SSO session status
|
||||
|
||||
### Recommended Solutions
|
||||
|
||||
**Option 1: Keycloak Configuration (Recommended)**
|
||||
- Enable Front-Channel Logout for all clients
|
||||
- Configure Back-Channel Logout URLs
|
||||
- This ensures all applications are notified of logout
|
||||
|
||||
**Option 2: Clear SSO Session Cookie Explicitly**
|
||||
- After Admin API logout, redirect to Keycloak logout endpoint
|
||||
- Use `kc_action=LOGOUT` parameter (already implemented)
|
||||
- Ensure all client sessions are logged out first
|
||||
|
||||
**Option 3: Use Keycloak Single Logout (SLO)**
|
||||
- Configure all clients to participate in SLO
|
||||
- When dashboard logs out, all clients are automatically logged out
|
||||
- Requires Keycloak configuration changes
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### Issue 1: 30-Minute Inactivity Timeout
|
||||
- **Status**: Not implemented
|
||||
- **Reason**: NextAuth doesn't track activity, only session age
|
||||
- **Solution**: Client-side activity tracking + automatic logout
|
||||
|
||||
### Issue 2: Applications Still Connected
|
||||
- **Status**: Partially working
|
||||
- **Reason**: SSO session cookie persists even after client sessions are cleared
|
||||
- **Solution**: Keycloak configuration (Front-Channel Logout) or SLO
|
||||
|
||||
@ -280,8 +280,11 @@ export const authOptions: NextAuthOptions = {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -13,14 +13,26 @@ export default function SignIn() {
|
||||
const isLogoutRedirect = useRef(false);
|
||||
|
||||
// Check if this is a logout redirect (from Keycloak post_logout_redirect_uri)
|
||||
// OR if session was invalidated (e.g., from iframe logout)
|
||||
useEffect(() => {
|
||||
// Check URL parameters or session storage for logout flag
|
||||
const logoutParam = searchParams.get('logout');
|
||||
const fromLogout = sessionStorage.getItem('just_logged_out');
|
||||
const sessionInvalidated = sessionStorage.getItem('session_invalidated');
|
||||
|
||||
if (logoutParam === 'true' || fromLogout === 'true') {
|
||||
// Check if there's a NextAuth session cookie that's now invalid
|
||||
// This indicates the session was invalidated (e.g., by iframe logout)
|
||||
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='));
|
||||
|
||||
// If session was invalidated or this is a logout redirect, prevent auto-login
|
||||
if (logoutParam === 'true' || fromLogout === 'true' || sessionInvalidated === 'true') {
|
||||
isLogoutRedirect.current = true;
|
||||
sessionStorage.removeItem('just_logged_out');
|
||||
sessionStorage.removeItem('session_invalidated');
|
||||
|
||||
// Clear any OAuth parameters from URL to prevent callback processing
|
||||
const url = new URL(window.location.href);
|
||||
@ -42,7 +54,17 @@ export default function SignIn() {
|
||||
// Don't auto-trigger login after logout
|
||||
return;
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
// Detect session invalidation: if status becomes unauthenticated
|
||||
// and we had a session cookie, it means the session was invalidated
|
||||
if (status === 'unauthenticated' && hasInvalidSessionCookie && !hasAttemptedLogin.current) {
|
||||
console.log('Session invalidation detected (likely from iframe logout), preventing auto-login');
|
||||
sessionStorage.setItem('session_invalidated', 'true');
|
||||
isLogoutRedirect.current = true;
|
||||
// Don't auto-login - user must manually click "Se connecter"
|
||||
return;
|
||||
}
|
||||
}, [searchParams, status]);
|
||||
|
||||
useEffect(() => {
|
||||
// If user is already authenticated, redirect to home
|
||||
@ -51,8 +73,9 @@ export default function SignIn() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't auto-login if this is a logout redirect or we've already attempted login
|
||||
if (isLogoutRedirect.current || hasAttemptedLogin.current) {
|
||||
// Don't auto-login if this is a logout redirect, session was invalidated, or we've already attempted login
|
||||
const sessionInvalidated = sessionStorage.getItem('session_invalidated') === 'true';
|
||||
if (isLogoutRedirect.current || sessionInvalidated || hasAttemptedLogin.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -62,14 +85,36 @@ export default function SignIn() {
|
||||
}
|
||||
|
||||
// Only trigger Keycloak sign-in if not authenticated, not loading, and not from logout
|
||||
// Add a longer delay to ensure OAuth callbacks have completed
|
||||
// AND only if this is a truly new user (never logged in), not a session invalidation
|
||||
if (status === "unauthenticated") {
|
||||
// Check if there's evidence of a previous session (session cookie exists but invalid)
|
||||
// This indicates session was invalidated, not a new user
|
||||
const hasSessionCookie = document.cookie
|
||||
.split(';')
|
||||
.some(c => {
|
||||
const cookie = c.trim();
|
||||
return cookie.startsWith('next-auth.session-token=') ||
|
||||
cookie.startsWith('__Secure-next-auth.session-token=') ||
|
||||
cookie.startsWith('__Host-next-auth.session-token=');
|
||||
});
|
||||
|
||||
// If there's a session cookie but status is unauthenticated, session was invalidated
|
||||
// Don't auto-login in this case
|
||||
if (hasSessionCookie) {
|
||||
console.log('Session cookie detected but status is unauthenticated - session was invalidated, preventing auto-login');
|
||||
sessionStorage.setItem('session_invalidated', 'true');
|
||||
isLogoutRedirect.current = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Only auto-login for truly new users (no session cookie)
|
||||
hasAttemptedLogin.current = true;
|
||||
// Longer delay to ensure we're not in a logout redirect flow or OAuth callback
|
||||
const timer = setTimeout(() => {
|
||||
// Double-check we're still unauthenticated and not in a logout flow
|
||||
if (!isLogoutRedirect.current) {
|
||||
// Trigger Keycloak sign-in
|
||||
const stillInvalidated = sessionStorage.getItem('session_invalidated') === 'true';
|
||||
if (!isLogoutRedirect.current && !stillInvalidated) {
|
||||
// Trigger Keycloak sign-in only for new users
|
||||
signIn("keycloak", { callbackUrl: "/" });
|
||||
}
|
||||
}, 1000);
|
||||
@ -112,8 +157,11 @@ export default function SignIn() {
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
// Show logout message if coming from logout
|
||||
const showLogoutMessage = isLogoutRedirect.current || searchParams.get('logout') === 'true';
|
||||
// Show logout message if coming from logout or session was invalidated
|
||||
const sessionInvalidated = sessionStorage.getItem('session_invalidated') === 'true';
|
||||
const showLogoutMessage = isLogoutRedirect.current ||
|
||||
searchParams.get('logout') === 'true' ||
|
||||
sessionInvalidated;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -129,7 +177,9 @@ export default function SignIn() {
|
||||
<div>
|
||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-gray-900">
|
||||
{showLogoutMessage
|
||||
? "Vous avez été déconnecté avec succès"
|
||||
? (sessionInvalidated
|
||||
? "Votre session a expiré. Veuillez vous reconnecter."
|
||||
: "Vous avez été déconnecté avec succès")
|
||||
: initializationStatus === "initializing"
|
||||
? "Initialisation de votre espace..."
|
||||
: initializationStatus === "success"
|
||||
@ -142,8 +192,11 @@ export default function SignIn() {
|
||||
<div className="mt-4 text-center">
|
||||
<button
|
||||
onClick={() => {
|
||||
// Clear all logout/invalidation flags before logging in
|
||||
hasAttemptedLogin.current = false;
|
||||
isLogoutRedirect.current = false;
|
||||
sessionStorage.removeItem('just_logged_out');
|
||||
sessionStorage.removeItem('session_invalidated');
|
||||
// Force login prompt by adding prompt=login parameter
|
||||
// This ensures credentials are asked even if SSO session exists
|
||||
signIn("keycloak", {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user