NeahNew/app/api/auth/end-sso-session/route.ts

103 lines
3.3 KiB
TypeScript

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 }
);
}
}