2026-02-18 00:50:38 +05:30
|
|
|
'use client';
|
|
|
|
|
|
2026-03-08 01:55:46 +05:30
|
|
|
import { useEffect, useRef } from 'react';
|
2026-03-10 00:14:43 +05:30
|
|
|
import { logoutAction } from './actions';
|
2026-02-18 00:50:38 +05:30
|
|
|
|
|
|
|
|
export default function LogoutPage() {
|
2026-03-08 01:55:46 +05:30
|
|
|
const hasStarted = useRef(false);
|
|
|
|
|
|
2026-02-18 00:50:38 +05:30
|
|
|
useEffect(() => {
|
2026-03-08 01:55:46 +05:30
|
|
|
if (hasStarted.current) return;
|
|
|
|
|
hasStarted.current = true;
|
|
|
|
|
|
2026-03-10 00:14:43 +05:30
|
|
|
const performLogout = async () => {
|
|
|
|
|
// Set logout flag to prevent TokenSync/PresenceProvider from restoring tokens
|
|
|
|
|
localStorage.setItem('isLoggingOut', 'true');
|
2026-03-08 12:06:07 +05:30
|
|
|
|
2026-03-10 00:14:43 +05:30
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-18 00:50:38 +05:30
|
|
|
|
2026-03-10 00:14:43 +05:30
|
|
|
// Clear all localStorage auth data
|
|
|
|
|
localStorage.removeItem('accessToken');
|
|
|
|
|
localStorage.removeItem('refreshToken');
|
|
|
|
|
localStorage.removeItem('user');
|
|
|
|
|
|
2026-03-10 09:28:19 +05:30
|
|
|
// Manually clear non-httpOnly next-auth cookies as fallback
|
2026-03-10 00:14:43 +05:30
|
|
|
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`;
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-10 09:28:19 +05:30
|
|
|
// 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).
|
2026-03-10 00:14:43 +05:30
|
|
|
try {
|
|
|
|
|
await logoutAction();
|
2026-03-10 09:28:19 +05:30
|
|
|
} 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
|
2026-03-10 00:14:43 +05:30
|
|
|
window.location.href = '/login';
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
performLogout();
|
2026-02-18 00:50:38 +05:30
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="min-h-screen flex items-center justify-center bg-white">
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<div className="w-12 h-12 border-4 border-[#e58625] border-t-transparent rounded-full animate-spin mx-auto mb-6" />
|
|
|
|
|
<h1 className="font-fractul font-bold text-[24px] text-[#00293D] mb-2">
|
|
|
|
|
Signing Out
|
|
|
|
|
</h1>
|
|
|
|
|
<p className="font-serif text-[14px] text-[#00293D]/60">
|
|
|
|
|
Please wait while we securely log you out...
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|