NeahNew/AUTHENTICATION_FIXES.md
2026-01-01 17:40:16 +01:00

225 lines
6.5 KiB
Markdown

# 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**:
- `/signin` page had `useEffect(() => { signIn("keycloak") }, [])` that always triggered login
- No check for existing authentication status
- Keycloak logout endpoint was never called, leaving Keycloak cookies valid
**Fix Applied**:
1. **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"
2. **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
3. **Main navigation logout** (`components/main-nav.tsx`):
- Fixed to use `idToken` instead of `accessToken` for Keycloak logout
- Proper logout flow with Keycloak endpoint
---
### 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**:
1. **ID Token Storage** (`app/api/auth/options.ts`):
- Now stores `idToken` from Keycloak in JWT
- Exposes `idToken` in session object
- Preserves ID token during token refresh
2. **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
3. **Type Definitions** (`types/next-auth.d.ts`):
- Added `idToken` to Session and JWT interfaces
- Type-safe access to ID token
---
## Changes Made
### Files Modified
1. **`app/api/auth/options.ts`**
- Added `idToken` to JWT interface
- Store `account.id_token` in JWT during initial authentication
- Expose `idToken` in session callback
- Preserve `idToken` during token refresh
2. **`app/signin/page.tsx`**
- Added session status check
- Prevent auto-login if already authenticated
- Redirect authenticated users to home
3. **`components/auth/signout-handler.tsx`**
- Call Keycloak logout endpoint with ID token
- Proper logout flow that clears both NextAuth and Keycloak sessions
4. **`components/main-nav.tsx`**
- Fixed logout button to use `idToken` instead of `accessToken`
- Proper Keycloak logout flow
5. **`types/next-auth.d.ts`**
- Added `idToken?: string` to Session interface
- Added `idToken?: string` to JWT interface (both modules)
---
## 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:
```bash
# 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:
1. NextAuth cookies are cleared (dashboard logout)
2. Keycloak cookies are cleared (via logout endpoint)
3. 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:
1. Check browser console for errors
2. Verify `NEXT_PUBLIC_KEYCLOAK_ISSUER` is set correctly
3. Check that Keycloak logout endpoint is accessible
4. Verify ID token is present in session: `console.log(session?.idToken)`
### If iframes still log out independently:
1. Check Keycloak cookie domain configuration
2. Verify iframe apps are configured to use same Keycloak realm
3. Check browser cookie settings (third-party cookies may be blocked)
4. Verify Keycloak session timeout settings
---
**Date**: 2024
**Status**: ✅ Fixed
**Version**: 1.0