diff --git a/frontend/src/core/auth/Auth0Provider.tsx b/frontend/src/core/auth/Auth0Provider.tsx index 7678046..a1025dc 100644 --- a/frontend/src/core/auth/Auth0Provider.tsx +++ b/frontend/src/core/auth/Auth0Provider.tsx @@ -57,8 +57,9 @@ export const Auth0Provider: React.FC = ({ children }) => { // Component to inject token into API client with mobile support const TokenInjector: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const { getAccessTokenSilently, isAuthenticated, isLoading, user } = useAuth0(); + const { getAccessTokenSilently, isAuthenticated, isLoading, user, logout } = useAuth0(); const [retryCount, setRetryCount] = React.useState(0); + const validatingRef = React.useRef(false); // Basic component loading debug console.log('[TokenInjector] Component loaded'); @@ -116,6 +117,31 @@ const TokenInjector: React.FC<{ children: React.ReactNode }> = ({ children }) => return null; }; + // Prevent stale session state when cached token is no longer valid + React.useEffect(() => { + if (!isAuthenticated || isLoading || validatingRef.current) return; + + const validateToken = async () => { + validatingRef.current = true; + try { + await getAccessTokenSilently({ cacheMode: 'off', timeoutInSeconds: 10 }); + } catch (error: any) { + const errorType = error?.error || error?.message || ''; + if (errorType.includes('login_required') || errorType.includes('consent_required') || + errorType.includes('invalid_grant')) { + console.warn('[Auth] Stale token detected, clearing auth state'); + const { indexedDBStorage } = await import('../utils/indexeddb-storage'); + await indexedDBStorage.clearAll(); + logout({ openUrl: false }); + return; + } + } + validatingRef.current = false; + }; + + validateToken(); + }, [isAuthenticated, isLoading]); + // Force authentication check for devices when user seems logged in but isAuthenticated is false React.useEffect(() => { const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); diff --git a/frontend/src/core/utils/indexeddb-storage.ts b/frontend/src/core/utils/indexeddb-storage.ts index 6ce8dd6..3014e50 100644 --- a/frontend/src/core/utils/indexeddb-storage.ts +++ b/frontend/src/core/utils/indexeddb-storage.ts @@ -157,6 +157,23 @@ class IndexedDBStorage implements StorageAdapter, Auth0Cache { } } + async clearAll(): Promise { + await this.initPromise; + if (!this.db) { + this.memoryCache.clear(); + return; + } + const tx = this.db.transaction(this.storeName, 'readwrite'); + tx.objectStore(this.storeName).clear(); + await new Promise((resolve, reject) => { + tx.oncomplete = () => { + this.memoryCache.clear(); + resolve(); + }; + tx.onerror = () => reject(tx.error); + }); + } + key(index: number): string | null { const keys = Array.from(this.memoryCache.keys()); return keys[index] || null;