Skip to content

Conversation

@Nudelsuppe42
Copy link
Contributor

No description provided.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements OpenID Connect back-channel logout functionality for the dashboard application, allowing Keycloak to notify the application when a user's session should be invalidated. The implementation adds session tracking, logout token handling, and session invalidation logic.

Changes:

  • Implements a back-channel logout endpoint that accepts logout tokens from Keycloak
  • Adds session invalidation tracking using an in-memory global Set
  • Integrates session invalidation checks into the JWT callback flow
  • Updates middleware and components to handle invalidated sessions gracefully

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
apps/dashboard/typings/next-auth/next-auth.d.ts Adds optional sessionId field to JWT type for session tracking
apps/dashboard/src/util/invalidatedSessions.ts New utility module for tracking and checking invalidated sessions globally
apps/dashboard/src/util/auth.ts Integrates session invalidation checks, adds authorization scope configuration, updates error handling
apps/dashboard/src/middleware.ts Replaces default middleware with custom withAuth callback to block invalidated tokens
apps/dashboard/src/components/layout/index.tsx Adds optional chaining for safer access to session.user properties
apps/dashboard/src/components/layout/header/HeaderProfile.tsx Adds additional null checks for session.user.username
apps/dashboard/src/components/AuthProvider.tsx Updates logout logic to handle invalidated sessions and removes unused pathname check
apps/dashboard/src/app/(sideNavbar)/auth/signin/page.tsx Replaces auto-redirect with manual sign-in button
apps/dashboard/src/app/(sideNavbar)/api/auth/backchannel-logout/route.ts New endpoint to receive and process logout tokens from Keycloak

Comment on lines +20 to +22
// Decode the JWT to get the session ID (sid) without verification
const [, payload] = logoutToken.split('.');
const decodedPayload = JSON.parse(Buffer.from(payload, 'base64').toString());
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical security vulnerability: The JWT logout token is decoded without signature verification. This means the endpoint will accept logout tokens from any source, not just Keycloak. An attacker could craft a malicious JWT with any session ID or user ID to invalidate arbitrary sessions.

The JWT should be verified using Keycloak's public key before processing. Consider using a library like 'jose' or 'jsonwebtoken' to verify the token signature against Keycloak's JWKS endpoint.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +18
export async function POST(request: NextRequest) {
try {
const formData = await request.formData();
const logoutToken = formData.get('logout_token');

if (!logoutToken || typeof logoutToken !== 'string') {
return NextResponse.json({ error: 'Missing logout_token' }, { status: 400 });
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical security vulnerability: The backchannel-logout endpoint has no authentication or authorization checks. Any external party can send requests to this endpoint to invalidate arbitrary sessions.

This endpoint should be protected with authentication (e.g., verify the request comes from Keycloak using IP allowlists, shared secrets, or by verifying the JWT signature). According to OpenID Connect Back-Channel Logout specs, the logout token should be validated to ensure it comes from a trusted identity provider.

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +22
const [, payload] = logoutToken.split('.');
const decodedPayload = JSON.parse(Buffer.from(payload, 'base64').toString());
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The JWT payload decoding doesn't handle base64url padding correctly. JWTs use base64url encoding which may not include padding characters. The current code will throw an error if the payload doesn't have proper padding.

Use base64url decoding with padding handling, or replace '-' with '+', '_' with '/', and add padding if necessary before decoding.

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +34
export function markSessionAsChecked(sessionId: string | undefined, userId: string | undefined): void {
// Remove from invalidated sessions after checking to prevent memory buildup
if (sessionId) {
globalThis.invalidatedSessions?.delete(sessionId);
}
if (userId) {
globalThis.invalidatedSessions?.delete(userId);
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential memory leak: Sessions invalidated by userId (when sessionId is not present) are never cleaned up. The markSessionAsChecked function is only called when a token is validated, but if a user has multiple sessions or the session is never accessed after invalidation, the userId entry will remain in the Set indefinitely.

Consider implementing a time-based expiration strategy or limiting the size of the invalidated sessions Set to prevent unbounded memory growth.

Copilot uses AI. Check for mistakes.
};
} catch (error) {
console.error('Failed to refresh access token', error);
console.log('Failed to refresh access token', error);
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing console.error to console.log reduces error visibility in production logs and monitoring systems. Error logging is important for debugging token refresh failures, especially in production environments where these errors indicate authentication issues.

Consider keeping console.error for actual error conditions to maintain proper log levels and facilitate debugging.

Suggested change
console.log('Failed to refresh access token', error);
console.error('Failed to refresh access token', error);

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +43
console.log('Back-channel logout received');

Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug console.log statement should be removed before merging to production. This log provides minimal value and will add noise to production logs.

Consider removing this log statement.

Suggested change
console.log('Back-channel logout received');

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +147
return {
expires: session.expires,
error: 'ForceLogout',
} as Session;
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential issue: The return type is cast to Session but the returned object only contains 'expires' and 'error', missing required Session fields like 'user'. While TypeScript will accept this due to the cast, it may cause runtime issues in components expecting a properly typed Session.

Consider checking if this partial Session object is properly handled by all consuming code, or define a more specific type that extends Session to include the error property without misleading type information.

Copilot uses AI. Check for mistakes.
issuer: process.env.NEXT_PUBLIC_KEYCLOAK_URL || '',
authorization: {
params: {
scope: 'openid email profile',
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The authorization scope has been explicitly set to 'openid email profile', but for back-channel logout to work properly with session_state tracking, the scope may need to include additional parameters. Verify that Keycloak is configured to include the session_state (sid) in the logout token with these scopes.

According to OpenID Connect Back-Channel Logout specifications, some identity providers require specific configuration or scopes to enable back-channel logout support.

Suggested change
scope: 'openid email profile',
// Include offline_access to better support session management and back-channel logout.
scope: 'openid email profile offline_access',

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature/frontend New feature or request

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants