Neah/app/loggedout/page.tsx
2025-05-02 12:33:43 +02:00

209 lines
7.3 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { clearAuthCookies } from "@/lib/session";
import Link from "next/link";
export default function LoggedOut() {
const [sessionStatus, setSessionStatus] = useState<'checking' | 'cleared' | 'error'>('checking');
// Listen for any messages from iframes
useEffect(() => {
const messageHandler = (event: MessageEvent) => {
// Handle any auth-related messages from iframes
if (event.data && event.data.type === 'AUTH_ERROR') {
console.log('Received auth error from iframe:', event.data);
}
};
window.addEventListener('message', messageHandler);
return () => window.removeEventListener('message', messageHandler);
}, []);
// Clear auth cookies again on this page as an extra precaution
useEffect(() => {
const checkAndClearSessions = async () => {
try {
// Additional browser storage clearing
console.log('Performing complete browser storage cleanup');
// Try to get any user ID from localStorage or sessionStorage for server-side cleanup
let userId = '';
try {
// Check standard localStorage locations for userId
const possibleUserIdKeys = [
'userId',
'user_id',
'currentUser',
'user',
'keycloak.userId',
'auth.userId'
];
for (const key of possibleUserIdKeys) {
const value = localStorage.getItem(key) || sessionStorage.getItem(key);
if (value) {
try {
// It might be a JSON object
const parsed = JSON.parse(value);
userId = parsed.id || parsed.userId || parsed.user_id || parsed.sub || '';
if (userId) break;
} catch {
// Or it might be a plain string
userId = value;
break;
}
}
}
console.log('Found user ID for server cleanup:', userId || 'None found');
} catch (e) {
console.error('Error getting user ID from storage:', e);
}
// Call the server-side cleanup if we have a user ID
if (userId) {
try {
const cleanupResponse = await fetch('/api/auth/session-cleanup', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ userId }),
credentials: 'include'
});
const cleanupResult = await cleanupResponse.json();
console.log('Server-side cleanup result:', cleanupResult);
} catch (e) {
console.error('Error calling server-side cleanup:', e);
}
}
// Clear cookies
clearAuthCookies();
// Clear session storage
try {
sessionStorage.clear();
console.log('Session storage cleared');
} catch (e) {
console.error('Error clearing session storage:', e);
}
// Clear local storage items related to auth
try {
const authLocalStoragePrefixes = ['token', 'auth', 'session', 'keycloak', 'kc', 'oidc', 'user', 'meteor'];
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);
}
}
}
console.log('Local storage auth items cleared');
} catch (e) {
console.error('Error clearing localStorage:', e);
}
// Double check for Keycloak specific cookies and chunked cookies
const cookies = document.cookie.split(';');
const cookieNames = cookies.map(cookie => cookie.split('=')[0].trim());
// Look for chunked cookies
const chunkedCookies = cookieNames.filter(name => /\.\d+$/.test(name));
const keycloakCookies = [
'KEYCLOAK_SESSION',
'KEYCLOAK_IDENTITY',
'KC_RESTART',
'rc_token',
'rc_uid',
'Meteor.loginToken',
...chunkedCookies
];
for (const cookieName of keycloakCookies) {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${window.location.hostname}; SameSite=None; Secure;`;
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
// Also try with root domain
const rootDomain = window.location.hostname.split('.').slice(-2).join('.');
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.${rootDomain}; SameSite=None; Secure;`;
}
// Notify any parent windows/iframes
try {
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'SESSION_CLEARED' }, '*');
}
} catch (e) {
console.error('Error notifying parent window:', e);
}
setSessionStatus('cleared');
} catch (error) {
console.error('Error during session cleanup:', error);
setSessionStatus('error');
}
};
checkAndClearSessions();
}, []);
return (
<div
className="min-h-screen flex items-center justify-center"
style={{
backgroundImage: "url('/signin.jpg')",
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat'
}}
>
<div className="w-full max-w-md p-8 bg-black/60 backdrop-blur-sm rounded-lg shadow-xl">
<div className="text-center">
<h2 className="text-3xl font-bold text-white mb-4">
You have been logged out
</h2>
{sessionStatus === 'checking' && (
<p className="text-white/80 mb-4">
Verifying all sessions are terminated...
</p>
)}
{sessionStatus === 'cleared' && (
<p className="text-white/80 mb-4">
Your session has been completely terminated and all authentication data has been cleared.
</p>
)}
{sessionStatus === 'error' && (
<p className="text-white/80 mb-4">
Your session has been terminated, but there might be some residual session data.
For complete security, please close your browser.
</p>
)}
<div className="mt-6">
<Link
href="/signin?signedOut=true"
className="inline-block px-8 py-3 bg-white text-gray-800 rounded hover:bg-gray-100 transition-colors mb-4"
>
Sign In Again
</Link>
<p className="text-white/60 text-sm mt-4">
Note: If you're automatically signed in again, try clearing your browser cookies or restarting your browser.
</p>
</div>
</div>
</div>
</div>
);
}