import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth/next'; import { authOptions } from '../options'; import { getKeycloakAdminClient } from '@/lib/keycloak'; import { jwtDecode } from 'jwt-decode'; /** * API endpoint to end the Keycloak SSO session for the current user * This uses Keycloak Admin API to explicitly logout the user from all sessions, * which clears the realm-wide SSO session, not just the client session. * * This ensures that when a user logs out from the dashboard, they are also * logged out from all other applications that share the same Keycloak realm. */ export async function POST(request: NextRequest) { try { // Get the current session const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json( { error: 'Unauthorized', message: 'No active session' }, { status: 401 } ); } // Get the ID token to extract user information const idToken = session.idToken; if (!idToken) { return NextResponse.json( { error: 'Missing ID token', message: 'Cannot end SSO session without ID token' }, { status: 400 } ); } // Decode the ID token to get the user's Keycloak subject (user ID) let userId: string; try { const decoded = jwtDecode<{ sub: string }>(idToken); userId = decoded.sub; } catch (error) { console.error('Error decoding ID token:', error); return NextResponse.json( { error: 'Invalid ID token', message: 'Failed to decode ID token' }, { status: 400 } ); } // Get Keycloak Admin Client let adminClient; try { adminClient = await getKeycloakAdminClient(); } catch (error) { console.error('Error getting Keycloak admin client:', error); return NextResponse.json( { error: 'Keycloak admin error', message: 'Failed to connect to Keycloak admin API' }, { status: 500 } ); } // Logout the user from all sessions using Admin API // This will end the SSO session, not just the client session try { await adminClient.users.logout({ id: userId }); console.log(`Successfully ended SSO session for user: ${userId}`); return NextResponse.json({ success: true, message: 'SSO session ended successfully', userId: userId }); } catch (error: any) { console.error('Error ending SSO session:', error); // If the error is that the user doesn't exist or session doesn't exist, // that's okay - they're already logged out if (error?.response?.status === 404 || error?.status === 404) { return NextResponse.json({ success: true, message: 'User session not found (already logged out)', userId: userId }); } return NextResponse.json( { error: 'Failed to end SSO session', message: error?.message || 'Unknown error', details: error?.response?.data || error }, { status: 500 } ); } } catch (error) { console.error('Unexpected error in end-sso-session endpoint:', error); return NextResponse.json( { error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 } ); } }