From 53aad2701b96c6d9496f36ef9ef819cef4dbcf7f Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Mon, 16 Feb 2026 16:50:19 -0500 Subject: [PATCH] feat(frontend): enhance 401 handling to clear tokens and redirect - Update fetch interceptor to special-case 401 responses - Clear invalid tokens from localStorage on 401 (token, refreshToken, user) - Distinguish between page navigation and API calls: - Page navigation: throw error to prevent further processing - API calls: show toast error with session expired message - Suppress network error toast for redirect errors - Compile TypeScript to JavaScript This ensures frontend properly handles expired sessions by clearing stale credentials and showing appropriate error messages. --- web/src/toast.ts | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/web/src/toast.ts b/web/src/toast.ts index 3d51f38..2d35071 100644 --- a/web/src/toast.ts +++ b/web/src/toast.ts @@ -160,15 +160,43 @@ const setupFetchInterceptor = (): void => { window.fetch = async (...args: Parameters): Promise => { try { const response = await originalFetch(...args); - + + // Special handling for 401 Unauthorized + if (response.status === 401) { + // Clear invalid tokens from localStorage + localStorage.removeItem('token'); + localStorage.removeItem('refreshToken'); + localStorage.removeItem('user'); + + // Check if this was a page navigation (not API call) + const url = args[0] as string; + + // Don't show toast for page navigations - will be handled by redirect + if (!url.startsWith('/api/')) { + // Direct navigation to protected page will be caught by middleware + // Just throw to prevent further processing + throw new Error('Session expired'); + } + + // API call - show toast error + const errorMessage = await parseFetchError(response); + showToast(errorMessage, 'error'); + + return response; + } + + // Handle other errors if (!response.ok) { const errorMessage = await parseFetchError(response); showToast(errorMessage, 'error'); } - + return response; } catch (error) { - showToast('Network error: Unable to connect to server', 'error'); + // Don't show toast for redirect errors + if ((error as Error).message !== 'Session expired') { + showToast('Network error: Unable to connect to server', 'error'); + } throw error; } };