From 9c2a00cd42084f6214ea688a0177f51927f9f549 Mon Sep 17 00:00:00 2001 From: adityapat24 Date: Thu, 26 Mar 2026 12:45:58 -0400 Subject: [PATCH 1/2] logout when cookies not present --- frontend/src/api.ts | 23 ++++++++++++- frontend/src/context/auth/authContext.tsx | 42 +++++++++++++++++++++-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 06878c34..7cc4349a 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1,6 +1,21 @@ // API INDEX const BASE = (import.meta.env.VITE_API_URL || '').replace(/\/$/, ''); +export const COOKIE_MISSING_EVENT = 'bcan:cookie-missing'; +let hasDispatchedCookieMissingEvent = false; + +function notifyCookieMissing(path: string): void { + if (hasDispatchedCookieMissingEvent || typeof window === 'undefined') { + return; + } + + hasDispatchedCookieMissingEvent = true; + window.dispatchEvent( + new CustomEvent(COOKIE_MISSING_EVENT, { + detail: { path }, + }) + ); +} export async function api( path: string, @@ -9,8 +24,14 @@ export async function api( const cleanPath = path.startsWith('/') ? path : `/${path}`; const url = `${BASE}${cleanPath}`; - return fetch(url, { + const response = await fetch(url, { credentials: 'include', // ← send & receive the jwt cookie ...init, }); + + if (response.status === 401) { + notifyCookieMissing(cleanPath); + } + + return response; } diff --git a/frontend/src/context/auth/authContext.tsx b/frontend/src/context/auth/authContext.tsx index 82cd7eb9..2cbaed6b 100644 --- a/frontend/src/context/auth/authContext.tsx +++ b/frontend/src/context/auth/authContext.tsx @@ -1,10 +1,11 @@ -import { useContext, createContext, ReactNode, useEffect, useRef } from 'react'; +import { useContext, createContext, ReactNode, useEffect, useRef, useState } from 'react'; import { getAppStore } from '../../external/bcanSatchel/store'; import { setAuthState, logoutUser } from '../../external/bcanSatchel/actions'; import { observer } from 'mobx-react-lite'; import { User } from '../../../../middle-layer/types/User'; -import { api } from '../../api'; +import { api, COOKIE_MISSING_EVENT } from '../../api'; import { fetchUsers } from '../../main-page/users/UserActions.ts'; +import Button from '../../components/Button'; /** @@ -32,6 +33,7 @@ export const useAuthContext = () => { export const AuthProvider = observer(({ children }: { children: ReactNode }) => { const store = getAppStore(); const logoutTimerRef = useRef | null>(null); + const [showCookieErrorPrompt, setShowCookieErrorPrompt] = useState(false); // Auto-logout timeout duration (in milliseconds) // 8 hours = 8 * 60 * 60 * 1000 const SESSION_TIMEOUT = 8 * 60 * 60 * 1000; @@ -135,6 +137,26 @@ export const AuthProvider = observer(({ children }: { children: ReactNode }) => }; }, [store.isAuthenticated]); + useEffect(() => { + const onCookieMissing = () => { + if (store.isAuthenticated) { + setShowCookieErrorPrompt(true); + } + }; + + window.addEventListener(COOKIE_MISSING_EVENT, onCookieMissing); + + return () => { + window.removeEventListener(COOKIE_MISSING_EVENT, onCookieMissing); + }; + }, [store.isAuthenticated]); + + useEffect(() => { + if (!store.isAuthenticated && showCookieErrorPrompt) { + setShowCookieErrorPrompt(false); + } + }, [showCookieErrorPrompt, store.isAuthenticated]); + /** Restore user session on refresh */ // useEffect(() => { // api('/auth/session') @@ -154,6 +176,22 @@ export const AuthProvider = observer(({ children }: { children: ReactNode }) => }} > {children} + {showCookieErrorPrompt && ( +
+
+

Internal Error

+

+ An internal error occurred and your session could not be verified. + Please log out and log back in. +

+
+
+ )} ); }); From c5ca774100b66105e2b8d3eb290101c249c8bbd6 Mon Sep 17 00:00:00 2001 From: adityapat24 Date: Thu, 26 Mar 2026 19:42:57 -0400 Subject: [PATCH 2/2] comment --- frontend/src/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/api.ts b/frontend/src/api.ts index ed7bedab..ba2596be 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -47,7 +47,7 @@ export async function api( const url = `${BASE}${cleanPath}`; const response = await fetch(url, { - credentials: 'include', // ← send & receive the jwt cookie + credentials: 'include', ...init, });