6.5 KiB
Authentication Flow Fixes
Issues Fixed
1. Logout Loop Issue ✅
Problem:
- User couldn't log out - infinite redirect loop
- Sign-in page auto-triggered Keycloak login even when user was already authenticated
- Keycloak session cookies weren't cleared, causing immediate re-authentication
Root Cause:
/signinpage haduseEffect(() => { signIn("keycloak") }, [])that always triggered login- No check for existing authentication status
- Keycloak logout endpoint was never called, leaving Keycloak cookies valid
Fix Applied:
-
Sign-in page (
app/signin/page.tsx):- Added check for existing session before triggering login
- If user is already authenticated, redirect to home
- Only trigger Keycloak login if status is "unauthenticated"
-
Sign-out handler (
components/auth/signout-handler.tsx):- Now properly calls Keycloak logout endpoint
- Uses ID token for proper logout
- Clears both NextAuth and Keycloak cookies
-
Main navigation logout (
components/main-nav.tsx):- Fixed to use
idTokeninstead ofaccessTokenfor Keycloak logout - Proper logout flow with Keycloak endpoint
- Fixed to use
2. Iframe Applications Logging Out ✅
Problem:
- Iframe applications were logging out even when user was still authenticated in dashboard
- Desynchronization between NextAuth session and Keycloak session
Root Cause:
- Sign-out only cleared NextAuth cookies
- Keycloak session cookies remained valid but could expire independently
- Iframe apps rely on Keycloak cookies for SSO
- When Keycloak cookies expired/invalidated, iframes logged out but dashboard stayed logged in
Fix Applied:
-
ID Token Storage (
app/api/auth/options.ts):- Now stores
idTokenfrom Keycloak in JWT - Exposes
idTokenin session object - Preserves ID token during token refresh
- Now stores
-
Proper Keycloak Logout:
- Sign-out now calls Keycloak logout endpoint with
id_token_hint - This properly invalidates Keycloak session and clears Keycloak cookies
- Ensures synchronization between dashboard and iframe apps
- Sign-out now calls Keycloak logout endpoint with
-
Type Definitions (
types/next-auth.d.ts):- Added
idTokento Session and JWT interfaces - Type-safe access to ID token
- Added
Changes Made
Files Modified
-
app/api/auth/options.ts- Added
idTokento JWT interface - Store
account.id_tokenin JWT during initial authentication - Expose
idTokenin session callback - Preserve
idTokenduring token refresh
- Added
-
app/signin/page.tsx- Added session status check
- Prevent auto-login if already authenticated
- Redirect authenticated users to home
-
components/auth/signout-handler.tsx- Call Keycloak logout endpoint with ID token
- Proper logout flow that clears both NextAuth and Keycloak sessions
-
components/main-nav.tsx- Fixed logout button to use
idTokeninstead ofaccessToken - Proper Keycloak logout flow
- Fixed logout button to use
-
types/next-auth.d.ts- Added
idToken?: stringto Session interface - Added
idToken?: stringto JWT interface (both modules)
- Added
How It Works Now
Sign-In Flow (Fixed)
1. User navigates to /signin
2. Check session status:
- If authenticated → Redirect to /
- If unauthenticated → Trigger Keycloak login
3. After Keycloak authentication:
- Store tokens (access, refresh, ID token)
- Initialize storage
- Redirect to dashboard
Sign-Out Flow (Fixed)
1. User clicks logout
2. Sign out from NextAuth (clears NextAuth cookies)
3. Call Keycloak logout endpoint:
- URL: ${KEYCLOAK_ISSUER}/protocol/openid-connect/logout
- Parameters:
* post_logout_redirect_uri: /signin
* id_token_hint: <ID token from session>
4. Keycloak clears its session and cookies
5. Redirect to /signin (no auto-login loop)
Iframe SSO (Fixed)
1. User authenticates in dashboard
2. Keycloak sets session cookies
3. Iframe apps read Keycloak cookies
4. When user logs out:
- Keycloak logout endpoint is called
- Keycloak cookies are cleared
- Iframe apps lose access (synchronized logout)
Environment Variables Required
Ensure these are set:
# Required for logout
NEXT_PUBLIC_KEYCLOAK_ISSUER=https://keycloak.example.com/realms/neah
# Already required for authentication
KEYCLOAK_CLIENT_ID=neah-dashboard
KEYCLOAK_CLIENT_SECRET=<secret>
KEYCLOAK_ISSUER=https://keycloak.example.com/realms/neah
NEXTAUTH_URL=https://dashboard.example.com
NEXTAUTH_SECRET=<secret>
Important: NEXT_PUBLIC_KEYCLOAK_ISSUER must be set for client-side logout to work.
Testing Checklist
Logout Flow
- Click logout button
- Should redirect to Keycloak logout
- Should redirect back to /signin
- Should NOT auto-login (no loop)
- Should be able to manually log in again
Sign-In Flow
- Navigate to /signin when not authenticated
- Should trigger Keycloak login
- Navigate to /signin when already authenticated
- Should redirect to / (no auto-login trigger)
Iframe SSO
- Log in to dashboard
- Open iframe application
- Should be automatically authenticated
- Log out from dashboard
- Iframe application should also lose authentication
- Refresh iframe - should require login
Additional Notes
ID Token vs Access Token
- Access Token: Used for API calls to Keycloak-protected resources
- ID Token: Used for user identification and logout
- Refresh Token: Used to get new access tokens
The ID token is required for proper Keycloak logout. It tells Keycloak which session to invalidate.
Cookie Synchronization
The fix ensures that:
- NextAuth cookies are cleared (dashboard logout)
- Keycloak cookies are cleared (via logout endpoint)
- Both happen in sequence, maintaining synchronization
Token Refresh
During token refresh, the ID token is preserved (Keycloak doesn't issue new ID tokens on refresh). This ensures logout continues to work even after token refreshes.
Troubleshooting
If logout still loops:
- Check browser console for errors
- Verify
NEXT_PUBLIC_KEYCLOAK_ISSUERis set correctly - Check that Keycloak logout endpoint is accessible
- Verify ID token is present in session:
console.log(session?.idToken)
If iframes still log out independently:
- Check Keycloak cookie domain configuration
- Verify iframe apps are configured to use same Keycloak realm
- Check browser cookie settings (third-party cookies may be blocked)
- Verify Keycloak session timeout settings
Date: 2024
Status: ✅ Fixed
Version: 1.0