import { Session } from "next-auth"; import { JWT } from "next-auth/jwt"; import { DefaultSession } from "next-auth"; export interface ServiceToken { token: string; userId: string; expiresAt: number; } export interface ExtendedSession extends DefaultSession { user: { id: string; name?: string | null; email?: string | null; image?: string | null; username: string; first_name: string; last_name: string; role: string[]; }; accessToken: string; refreshToken?: string; serviceTokens: { rocketChat?: ServiceToken; leantime?: ServiceToken; calendar?: ServiceToken; mail?: ServiceToken; [key: string]: ServiceToken | undefined; }; expires: string; } export interface ExtendedJWT extends JWT { accessToken?: string; refreshToken?: string; accessTokenExpires?: number; role?: string[]; username?: string; first_name?: string; last_name?: string; name?: string | null; email?: string | null; serviceTokens: { rocketChat?: ServiceToken; leantime?: ServiceToken; calendar?: ServiceToken; mail?: ServiceToken; [key: string]: ServiceToken | undefined; }; } export async function invalidateServiceTokens(session: ExtendedSession) { const serviceEndpoints = { rocketChat: `${process.env.NEXT_PUBLIC_IFRAME_PAROLE_URL?.split('/channel')[0]}/api/v1/logout`, leantime: `${process.env.LEANTIME_API_URL}/api/jsonrpc`, // Add other service endpoints as needed }; const invalidatePromises = Object.entries(session.serviceTokens).map(async ([service, token]) => { if (!token) return; try { const endpoint = serviceEndpoints[service as keyof typeof serviceEndpoints]; if (!endpoint) return; await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(service === 'rocketChat' ? { 'X-Auth-Token': token.token, 'X-User-Id': token.userId, } : {}), ...(service === 'leantime' ? { 'X-API-Key': process.env.LEANTIME_TOKEN!, } : {}), }, body: service === 'leantime' ? JSON.stringify({ jsonrpc: '2.0', method: 'leantime.rpc.auth.logout', id: 1 }) : undefined, }); } catch (error) { console.error(`Error invalidating ${service} token:`, error); } }); await Promise.all(invalidatePromises); } export function clearAuthCookies() { const cookies = document.cookie.split(';'); console.log('Clearing all auth cookies'); // List of known auth-related cookie prefixes const authCookiePrefixes = [ 'next-auth.', '__Secure-next-auth.', '__Host-next-auth.', 'KEYCLOAK_', 'KC_', 'JSESSIONID', 'OAuth_Token_Request_State', 'OAUTH2_CLIENT_ID', 'OAUTH2_STATE', 'XSRF-TOKEN' ]; for (const cookie of cookies) { const [name] = cookie.split('='); const trimmedName = name.trim(); // Check if this is an auth-related cookie const isAuthCookie = authCookiePrefixes.some(prefix => trimmedName.startsWith(prefix) ); // Also clear cookies with auth-related terms const containsAuthTerm = trimmedName.toLowerCase().includes('auth') || trimmedName.toLowerCase().includes('token') || trimmedName.toLowerCase().includes('session'); if (isAuthCookie || containsAuthTerm) { console.log(`Clearing cookie: ${trimmedName}`); // Clear the cookie with various domain/path combinations // Standard path document.cookie = `${trimmedName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; // Root domain const domain = window.location.hostname.split('.').slice(-2).join('.'); document.cookie = `${trimmedName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain};`; // Full domain document.cookie = `${trimmedName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${window.location.hostname};`; } } // Clear localStorage items that might be related to authentication try { const authLocalStoragePrefixes = ['token', 'auth', 'session', 'keycloak', 'kc', 'user']; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key) { const keyLower = key.toLowerCase(); if (authLocalStoragePrefixes.some(prefix => keyLower.includes(prefix))) { console.log(`Clearing localStorage: ${key}`); localStorage.removeItem(key); } } } } catch (e) { console.error('Error clearing localStorage:', e); } }