@@ -416,6 +416,21 @@ export function getAuthToken(): string | null {
416416 }
417417}
418418
419+ // Global logout handler for session expiry
420+ let globalLogoutHandler : ( ( ) => void ) | null = null ;
421+ export function setGlobalLogoutHandler ( handler : ( ) => void ) {
422+ globalLogoutHandler = handler ;
423+ }
424+
425+ // Clear auth tokens
426+ export function clearAuthTokens ( ) {
427+ try {
428+ localStorage . removeItem ( ACCESS_TOKEN_KEY_PRIMARY ) ;
429+ localStorage . removeItem ( ACCESS_TOKEN_KEY_FALLBACK ) ;
430+ localStorage . removeItem ( "cpl_current_user" ) ;
431+ } catch { }
432+ }
433+
419434async function apiFetchJson < T > ( path : string , init ?: JsonInit ) : Promise < T > {
420435 const { body, headers, ...rest } = init || { } ;
421436 const token = getAuthToken ( ) ;
@@ -424,6 +439,24 @@ async function apiFetchJson<T>(path: string, init?: JsonInit): Promise<T> {
424439 body : body !== undefined ? JSON . stringify ( body ) : undefined ,
425440 ...rest ,
426441 } ) ;
442+
443+ // Check for session expiry (401 Unauthorized or 403 Forbidden)
444+ if ( res . status === 401 || res . status === 403 ) {
445+ // Clear tokens
446+ clearAuthTokens ( ) ;
447+ // Trigger global logout handler if set
448+ if ( globalLogoutHandler ) {
449+ globalLogoutHandler ( ) ;
450+ }
451+ // Also trigger logout via BroadcastChannel for cross-tab communication
452+ try {
453+ const bc = new BroadcastChannel ( 'auth-updates' ) ;
454+ bc . postMessage ( { type : 'session-expired' } ) ;
455+ bc . close ( ) ;
456+ } catch { }
457+ throw new Error ( 'Session expired. Please login again.' ) ;
458+ }
459+
427460 if ( ! res . ok ) throw new Error ( `${ res . status } ${ res . statusText } ` ) ;
428461 // Handle 204 No Content
429462 if ( res . status === 204 ) return undefined as unknown as T ;
@@ -456,6 +489,21 @@ export async function uploadPlayerProfileImage(file: File): Promise<unknown> {
456489 fd . set ( "file" , file ) ;
457490 const token = getAuthToken ( ) ;
458491 const res = await fetch ( buildUrl ( API_PATHS . uploadPlayerProfile ) , { method : "POST" , headers : token ? { Authorization : `Bearer ${ token } ` } : undefined , body : fd } ) ;
492+
493+ // Check for session expiry
494+ if ( res . status === 401 || res . status === 403 ) {
495+ clearAuthTokens ( ) ;
496+ if ( globalLogoutHandler ) {
497+ globalLogoutHandler ( ) ;
498+ }
499+ try {
500+ const bc = new BroadcastChannel ( 'auth-updates' ) ;
501+ bc . postMessage ( { type : 'session-expired' } ) ;
502+ bc . close ( ) ;
503+ } catch { }
504+ throw new Error ( 'Session expired. Please login again.' ) ;
505+ }
506+
459507 if ( ! res . ok ) throw new Error ( `${ res . status } ${ res . statusText } ` ) ;
460508 return await res . json ( ) ;
461509}
0 commit comments