'use client'; import { useEffect, useRef } from 'react'; import { logoutAction } from './actions'; export default function LogoutPage() { const hasStarted = useRef(false); useEffect(() => { if (hasStarted.current) return; hasStarted.current = true; const performLogout = async () => { // Set logout flag to prevent TokenSync/PresenceProvider from restoring tokens localStorage.setItem('isLoggingOut', 'true'); // Invalidate refresh token on the backend (best-effort) const refreshToken = localStorage.getItem('refreshToken'); const accessToken = localStorage.getItem('accessToken'); if (refreshToken) { try { await fetch( `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api/v1'}/auth/logout`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), }, body: JSON.stringify({ refreshToken }), } ); } catch { // Best-effort — don't block logout if backend call fails } } // Clear all localStorage auth data localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); localStorage.removeItem('user'); // Manually clear non-httpOnly next-auth cookies as fallback const cookieNames = [ 'authjs.session-token', 'authjs.callback-url', 'authjs.csrf-token', '__Secure-authjs.session-token', '__Secure-authjs.callback-url', '__Secure-authjs.csrf-token', 'next-auth.session-token', 'next-auth.callback-url', 'next-auth.csrf-token', '__Secure-next-auth.session-token', ]; cookieNames.forEach((name) => { document.cookie = `${name}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT`; document.cookie = `${name}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure`; }); // Use server action to properly clear the httpOnly session cookie. // signOut() internally calls redirect() which throws NEXT_REDIRECT — // Next.js handles this automatically. We should NOT catch that error. // Only use fallback if the action truly fails (not a redirect throw). try { await logoutAction(); } catch (error: unknown) { // In Next.js App Router, redirect() throws an error with digest containing "NEXT_REDIRECT". // This is normal behavior — let it propagate so Next.js handles the redirect. const isRedirectError = error instanceof Error && 'digest' in error && typeof (error as { digest?: string }).digest === 'string' && (error as { digest: string }).digest.includes('NEXT_REDIRECT'); if (isRedirectError) { // Re-throw so Next.js handles the redirect properly throw error; } // Genuine error — use full page navigation as fallback window.location.href = '/login'; } }; performLogout(); }, []); return (
Please wait while we securely log you out...