NeahNew/SEPARATED_AUTHENTICATION_FLOWS_EXPLANATION.md

14 KiB

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

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:

  • 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