diff --git a/backend/src/modules/auth/auth.controller.ts b/backend/src/modules/auth/auth.controller.ts index 07f85de..afc0b62 100644 --- a/backend/src/modules/auth/auth.controller.ts +++ b/backend/src/modules/auth/auth.controller.ts @@ -136,6 +136,28 @@ export class AuthController { return this.authService.getProfile(req.user.id); } + // ========================= + // LOGOUT + // ========================= + @Post('logout') + @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Logout current user' }) + @ApiResponse({ status: 200, description: 'Logout successful' }) + async logout(@Req() req, @Res({ passthrough: true }) res: Response) { + const accessToken = req?.cookies?.accessToken; + + if (accessToken) { + await this.authService.logout(accessToken); + } + + this.clearAuthCookies(res); + + return { + success: true, + message: 'Logged out successfully', + }; + } + // ========================= // TEST // ========================= @@ -174,4 +196,19 @@ export class AuthController { path: '/', }); } + + private clearAuthCookies(res: Response) { + res.clearCookie('accessToken', { + httpOnly: true, + secure: false, + sameSite: 'lax', + path: '/', + }); + res.clearCookie('refreshToken', { + httpOnly: true, + secure: false, + sameSite: 'lax', + path: '/', + }); + } } \ No newline at end of file diff --git a/frontend/src/lib/hooks/useAuth.tsx b/frontend/src/lib/hooks/useAuth.tsx index a97d5b0..58be0ac 100644 --- a/frontend/src/lib/hooks/useAuth.tsx +++ b/frontend/src/lib/hooks/useAuth.tsx @@ -181,11 +181,22 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { }, [router]); const logout = useCallback(async () => { - localStorage.clear(); - setUser(null); - setOrganizations([]); - setCurrentOrganization(null); - router.push('/'); + try { + // Important: clear auth cookies/session on the server first, + // otherwise middleware may still treat the user as authenticated. + await authApi.logout(); + } catch (err) { + console.error('Logout API failed:', err); + } finally { + localStorage.clear(); + setUser(null); + setOrganizations([]); + setCurrentOrganization(null); + setError(null); + setIsAuthReady(true); + router.replace('/'); + router.refresh(); + } }, [router]); const selectOrganization = useCallback(async (orgId: string) => {