367 lines
14 KiB
Markdown
367 lines
14 KiB
Markdown
# Why Dashboard and Applications Have Separated Authentication Flows
|
|
|
|
## Executive Summary
|
|
|
|
The dashboard and applications use **two completely separate authentication mechanisms** that operate independently:
|
|
|
|
1. **Dashboard**: Uses **NextAuth.js** with JWT-based sessions (30 days)
|
|
2. **Applications**: Use **Keycloak SSO** directly via browser cookies
|
|
|
|
This separation is why logging out from the dashboard doesn't automatically log you out from applications opened directly in the browser.
|
|
|
|
---
|
|
|
|
## Architecture Overview
|
|
|
|
### Two Independent Authentication Systems
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ AUTHENTICATION LAYERS │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
|
│ │ DASHBOARD AUTH │ │ APPLICATION AUTH │ │
|
|
│ │ │ │ │ │
|
|
│ │ NextAuth.js │ │ Keycloak SSO │ │
|
|
│ │ (JWT Strategy) │ │ (Cookie-based) │ │
|
|
│ │ │ │ │ │
|
|
│ │ - Session: 30 days │ │ - Session: Variable │ │
|
|
│ │ - Stored in: Cookie │ │ - Stored in: Cookie │ │
|
|
│ │ - Domain: Dashboard │ │ - Domain: Keycloak │ │
|
|
│ │ - Independent │ │ - Independent │ │
|
|
│ └──────────────────────┘ └──────────────────────┘ │
|
|
│ │ │ │
|
|
│ └──────────┬───────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────▼────────┐ │
|
|
│ │ KEYCLOAK │ │
|
|
│ │ (IdP Server) │ │
|
|
│ └────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Why They're Separated
|
|
|
|
### 1. Different Authentication Purposes
|
|
|
|
**Dashboard Authentication (NextAuth.js)**:
|
|
- Purpose: Authenticate the **Next.js dashboard application**
|
|
- Method: OAuth 2.0 flow → Get tokens → Store in JWT
|
|
- Session Management: NextAuth manages its own session lifecycle
|
|
- Storage: Encrypted JWT in HTTP-only cookie on dashboard domain
|
|
- Duration: 30 days (configurable in `app/api/auth/options.ts`)
|
|
|
|
**Application Authentication (Keycloak SSO)**:
|
|
- Purpose: Authenticate **standalone applications** (not embedded in dashboard)
|
|
- Method: Direct Keycloak authentication via browser cookies
|
|
- Session Management: Keycloak manages SSO session lifecycle
|
|
- Storage: Keycloak session cookies on Keycloak domain
|
|
- Duration: Configured in Keycloak (typically 30 minutes to a few hours)
|
|
|
|
### 2. Different Session Storage Locations
|
|
|
|
**Dashboard Session**:
|
|
```
|
|
Cookie Name: next-auth.session-token
|
|
Domain: dashboard.example.com
|
|
Path: /
|
|
HttpOnly: Yes
|
|
Secure: Yes (if HTTPS)
|
|
SameSite: Lax
|
|
Content: Encrypted JWT containing:
|
|
- accessToken (Keycloak OAuth token)
|
|
- refreshToken (Keycloak refresh token)
|
|
- idToken (Keycloak ID token)
|
|
- User info (id, email, roles, etc.)
|
|
```
|
|
|
|
**Application Session**:
|
|
```
|
|
Cookie Name: KEYCLOAK_SESSION
|
|
Domain: keycloak.example.com (or configured domain)
|
|
Path: /
|
|
HttpOnly: Yes
|
|
Secure: Yes
|
|
SameSite: Lax or None (for cross-site)
|
|
Content: Keycloak session identifier
|
|
```
|
|
|
|
### 3. Different Authentication Flows
|
|
|
|
**Dashboard Flow**:
|
|
```
|
|
1. User visits dashboard → /signin
|
|
2. NextAuth redirects to Keycloak OAuth endpoint
|
|
3. Keycloak authenticates user
|
|
4. Keycloak redirects back with authorization code
|
|
5. NextAuth exchanges code for tokens
|
|
6. NextAuth creates JWT session
|
|
7. JWT stored in dashboard cookie
|
|
8. Dashboard uses JWT for authentication
|
|
```
|
|
|
|
**Application Flow** (when opened directly):
|
|
```
|
|
1. User visits application directly (not via dashboard)
|
|
2. Application checks for Keycloak session cookie
|
|
3. If cookie exists → User is authenticated (SSO)
|
|
4. If cookie doesn't exist → Redirect to Keycloak login
|
|
5. Keycloak authenticates user
|
|
6. Keycloak sets session cookie
|
|
7. Application uses cookie for authentication
|
|
```
|
|
|
|
---
|
|
|
|
## Why Dashboard Logout Doesn't Log Out Applications
|
|
|
|
### The Problem
|
|
|
|
When you log out from the dashboard:
|
|
|
|
1. **Dashboard logout process**:
|
|
- Clears NextAuth session cookie (`next-auth.session-token`)
|
|
- Calls Keycloak logout endpoint with `id_token_hint`
|
|
- Keycloak clears **client session** for dashboard OAuth client
|
|
- Keycloak may clear SSO session (if it's the last client session)
|
|
|
|
2. **What happens to applications**:
|
|
- Applications don't know about dashboard logout
|
|
- Applications still have Keycloak SSO session cookie
|
|
- Applications continue to work because they use Keycloak cookies, not NextAuth
|
|
|
|
### Technical Reasons
|
|
|
|
#### Reason 1: Different Cookie Domains
|
|
|
|
**Dashboard Cookie**:
|
|
- Domain: `dashboard.example.com`
|
|
- Cleared when dashboard logs out
|
|
- Applications can't access this cookie (different domain)
|
|
|
|
**Keycloak SSO Cookie**:
|
|
- Domain: `keycloak.example.com` (or configured domain)
|
|
- Not cleared by dashboard logout (unless SSO session is cleared)
|
|
- Applications can access this cookie (same domain as Keycloak)
|
|
|
|
#### Reason 2: Independent Session Lifecycles
|
|
|
|
**NextAuth Session**:
|
|
- Managed by NextAuth.js
|
|
- Lifecycle: Created on login → Valid for 30 days → Cleared on logout
|
|
- Independent of Keycloak SSO session
|
|
|
|
**Keycloak SSO Session**:
|
|
- Managed by Keycloak server
|
|
- Lifecycle: Created on login → Valid until timeout or explicit logout → Cleared on logout
|
|
- Independent of NextAuth session
|
|
|
|
#### Reason 3: Different Authentication Mechanisms
|
|
|
|
**Dashboard**:
|
|
- Uses OAuth 2.0 tokens (access token, refresh token)
|
|
- Tokens stored in NextAuth JWT
|
|
- Authentication: Validate JWT → Extract tokens → Use tokens for API calls
|
|
|
|
**Applications**:
|
|
- Use Keycloak session cookies directly
|
|
- No OAuth tokens involved
|
|
- Authentication: Check for Keycloak session cookie → If exists, user is authenticated
|
|
|
|
#### Reason 4: Keycloak SSO Session Persistence
|
|
|
|
**Keycloak maintains two types of sessions**:
|
|
|
|
1. **Client Session** (per OAuth client):
|
|
- Specific to each OAuth client (dashboard, app1, app2, etc.)
|
|
- Cleared when that specific client logs out
|
|
- Dashboard logout clears dashboard's client session
|
|
|
|
2. **SSO Session** (realm-wide):
|
|
- Shared across all clients in the realm
|
|
- Persists even after individual client logouts
|
|
- Only cleared when:
|
|
- All client sessions are logged out
|
|
- Explicit SSO session logout
|
|
- Session timeout
|
|
- Admin API logout
|
|
|
|
**When dashboard logs out**:
|
|
- Dashboard's client session is cleared ✅
|
|
- SSO session may persist if other applications have active sessions ❌
|
|
- Applications continue to work because SSO session is still valid ❌
|
|
|
|
---
|
|
|
|
## Current Logout Flow Analysis
|
|
|
|
### What Happens When You Log Out from Dashboard
|
|
|
|
```
|
|
Step 1: User clicks logout in dashboard
|
|
↓
|
|
Step 2: Dashboard calls NextAuth signOut()
|
|
→ Clears: next-auth.session-token cookie
|
|
→ Clears: Dashboard's NextAuth session
|
|
↓
|
|
Step 3: Dashboard calls /api/auth/end-sso-session
|
|
→ Uses Keycloak Admin API
|
|
→ Calls: adminClient.users.logout({ id: userId })
|
|
→ Clears: All client sessions for user
|
|
→ May clear: SSO session (if it's the last client session)
|
|
↓
|
|
Step 4: Dashboard redirects to Keycloak logout endpoint
|
|
→ URL: ${KEYCLOAK_ISSUER}/protocol/openid-connect/logout
|
|
→ Parameters: id_token_hint, post_logout_redirect_uri
|
|
→ Clears: Dashboard's client session
|
|
→ May clear: SSO session (if it's the last client session)
|
|
↓
|
|
Step 5: Keycloak redirects back to /signin?logout=true
|
|
→ Dashboard shows logout message
|
|
```
|
|
|
|
### What Happens to Applications
|
|
|
|
```
|
|
Applications opened directly in browser:
|
|
↓
|
|
Step 1: Application checks for Keycloak session cookie
|
|
→ Cookie: KEYCLOAK_SESSION
|
|
→ Domain: keycloak.example.com
|
|
↓
|
|
Step 2: If SSO session still exists:
|
|
→ Application finds valid SSO session cookie ✅
|
|
→ Application authenticates user automatically ✅
|
|
→ User remains logged in ❌
|
|
↓
|
|
Step 3: If SSO session was cleared:
|
|
→ Application doesn't find session cookie ✅
|
|
→ Application redirects to Keycloak login ✅
|
|
→ User must log in again ✅
|
|
```
|
|
|
|
### Why Applications Stay Logged In
|
|
|
|
**Scenario 1: SSO Session Persists**
|
|
- Dashboard logout clears client sessions
|
|
- But SSO session cookie still exists
|
|
- Applications check SSO session cookie → Still valid → User stays logged in
|
|
|
|
**Scenario 2: Other Applications Have Active Sessions**
|
|
- If other applications are open in other tabs/windows
|
|
- They have active client sessions
|
|
- Keycloak won't clear SSO session (because other clients are still active)
|
|
- All applications stay logged in
|
|
|
|
**Scenario 3: Cookie Domain Mismatch**
|
|
- Dashboard tries to clear Keycloak cookies client-side
|
|
- But cookies are on different domain (keycloak.example.com)
|
|
- Browser security prevents clearing cross-domain cookies
|
|
- Applications keep their cookies → Stay logged in
|
|
|
|
---
|
|
|
|
## Why This Architecture Exists
|
|
|
|
### Historical/Design Reasons
|
|
|
|
1. **Legacy Applications**:
|
|
- Applications may have existed before the dashboard
|
|
- They were designed to use Keycloak directly
|
|
- Dashboard was added later as a wrapper/portal
|
|
|
|
2. **Separation of Concerns**:
|
|
- Dashboard: Portal/aggregator (doesn't need to know about app internals)
|
|
- Applications: Standalone services (don't depend on dashboard)
|
|
|
|
3. **Flexibility**:
|
|
- Applications can be accessed directly (not just via dashboard)
|
|
- Applications can be used independently
|
|
- Dashboard is optional, not required
|
|
|
|
4. **SSO Design**:
|
|
- Keycloak SSO is designed to work across multiple applications
|
|
- Logging out from one app shouldn't log out from all apps
|
|
- This is by design for SSO functionality
|
|
|
|
### Technical Constraints
|
|
|
|
1. **Cookie Security**:
|
|
- Browsers prevent cross-domain cookie access
|
|
- Dashboard can't directly clear Keycloak cookies (different domain)
|
|
- Must use Keycloak logout endpoint or Admin API
|
|
|
|
2. **Stateless vs Stateful**:
|
|
- NextAuth: Stateless (JWT, no server-side session)
|
|
- Keycloak: Stateful (server-side session, cookies)
|
|
|
|
3. **OAuth vs Direct Authentication**:
|
|
- Dashboard: Uses OAuth 2.0 (tokens)
|
|
- Applications: Use direct Keycloak authentication (cookies)
|
|
|
|
---
|
|
|
|
## What Would Be Needed for Unified Logout
|
|
|
|
To make dashboard logout also log out applications, you would need:
|
|
|
|
### Option 1: Keycloak Front-Channel Logout (Recommended)
|
|
- Configure all applications to participate in Front-Channel Logout
|
|
- When dashboard logs out, Keycloak notifies all registered applications
|
|
- Applications receive logout notification and clear their sessions
|
|
- **Requires**: Keycloak configuration + Application support
|
|
|
|
### Option 2: Keycloak Single Logout (SLO)
|
|
- Configure all applications to participate in SLO
|
|
- When one application logs out, all applications are logged out
|
|
- **Requires**: Keycloak configuration + Application support
|
|
|
|
### Option 3: Clear SSO Session Explicitly
|
|
- Use Keycloak Admin API to end SSO session
|
|
- This clears the realm-wide SSO session
|
|
- All applications lose their authentication
|
|
- **Current Implementation**: Partially implemented (`/api/auth/end-sso-session`)
|
|
- **Issue**: May not clear SSO session cookie if other clients are active
|
|
|
|
### Option 4: Application Logout Endpoints
|
|
- Each application exposes a logout endpoint
|
|
- Dashboard calls all application logout endpoints
|
|
- Applications clear their own sessions
|
|
- **Requires**: Application modifications + Dashboard coordination
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
### Why They're Separated
|
|
|
|
1. **Different purposes**: Dashboard is a portal, applications are standalone services
|
|
2. **Different storage**: Dashboard uses NextAuth JWT, applications use Keycloak cookies
|
|
3. **Different domains**: Cookies are on different domains (security prevents cross-domain access)
|
|
4. **Different lifecycles**: NextAuth session (30 days) vs Keycloak SSO session (variable)
|
|
5. **SSO design**: Keycloak SSO is designed to persist across client logouts
|
|
|
|
### Why Dashboard Logout Doesn't Log Out Applications
|
|
|
|
1. **SSO session persists**: Keycloak SSO session may not be cleared
|
|
2. **Other active sessions**: If other applications are open, SSO session stays active
|
|
3. **Cookie domain**: Dashboard can't directly clear Keycloak cookies (different domain)
|
|
4. **Independent mechanisms**: Applications don't know about NextAuth session state
|
|
|
|
### The Solution
|
|
|
|
To achieve unified logout, you need to:
|
|
- Configure Keycloak Front-Channel Logout or SLO
|
|
- Ensure all applications participate in logout notifications
|
|
- Or use Admin API to explicitly end SSO session (current implementation attempts this)
|
|
|
|
The current implementation (`/api/auth/end-sso-session`) tries to clear the SSO session, but it may not work if:
|
|
- Other applications have active sessions
|
|
- SSO session cookie is on a different domain
|
|
- Keycloak configuration prevents SSO session clearing
|
|
|