feat: Implement a server-side logout action and enhance client-side logout with backend token invalidation and comprehensive local storage/cookie clearing.

This commit is contained in:
pradeepkumar
2026-03-10 00:14:43 +05:30
parent 7d641eec12
commit 69eff70fbb
2 changed files with 67 additions and 12 deletions

View File

@@ -0,0 +1,7 @@
'use server';
import { signOut } from '@/auth';
export async function logoutAction() {
await signOut({ redirectTo: '/login' });
}

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { signOut } from 'next-auth/react'; import { logoutAction } from './actions';
export default function LogoutPage() { export default function LogoutPage() {
const hasStarted = useRef(false); const hasStarted = useRef(false);
@@ -10,19 +10,67 @@ export default function LogoutPage() {
if (hasStarted.current) return; if (hasStarted.current) return;
hasStarted.current = true; hasStarted.current = true;
// Set logout flag to prevent TokenSync/PresenceProvider from restoring tokens const performLogout = async () => {
localStorage.setItem('isLoggingOut', 'true'); // Set logout flag to prevent TokenSync/PresenceProvider from restoring tokens
localStorage.setItem('isLoggingOut', 'true');
// Clear all localStorage auth data // Invalidate refresh token on the backend (best-effort)
localStorage.removeItem('accessToken'); const refreshToken = localStorage.getItem('refreshToken');
localStorage.removeItem('refreshToken'); const accessToken = localStorage.getItem('accessToken');
localStorage.removeItem('user'); 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
}
}
// Use signOut with callbackUrl - this ensures the server clears the // Clear all localStorage auth data
// session cookie BEFORE the redirect happens (server-side 302). localStorage.removeItem('accessToken');
// Do NOT use redirect:false + manual redirect, as that causes a race localStorage.removeItem('refreshToken');
// condition where navigation happens before the cookie is cleared. localStorage.removeItem('user');
signOut({ callbackUrl: '/login' });
// Manually clear next-auth cookies as fallback
// next-auth v5 uses these cookie names:
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 session (redirects to /login)
try {
await logoutAction();
} catch {
// Server action redirect throws in Next.js — this is expected.
// If it truly fails, the manual cookie clearing above ensures logout.
// Fallback redirect:
window.location.href = '/login';
}
};
performLogout();
}, []); }, []);
return ( return (