225 lines
6.5 KiB
Markdown
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
|
|
|