// Auth provider — SHOC shape (mirrors providers/auth-provider.tsx). // Session persistence is delegated to lib/auth/authStorage (sessionStorage, WEB-C1); // this provider only owns the in-memory state. Token acquisition (Cognito code // exchange, dev-login) stays in the auth pages — they hand the resolved AuthUser // to login(). import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'; import type { AuthUser } from '@proposal-system/api-contracts'; import { AUTH_SESSION_CLEARED_EVENT, clearAuth, getAuthUser, isTokenValid, setAuthUser, } from '../lib/auth/authStorage'; import { queryClient } from '../lib/queryClient'; import { AuthContext, type AuthContextValue } from './authContext'; export default function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(() => getAuthUser()); const [isLoading, setIsLoading] = useState(false); const [error, setErrorState] = useState(null); const login = useCallback((nextUser: AuthUser) => { setAuthUser(nextUser); setUser(nextUser); setErrorState(null); setIsLoading(false); }, []); const logout = useCallback(() => { clearAuth(); // Drop all principal-scoped state: cached queries must not survive into the // next login in the same tab, or query keys shared across users would serve // the previous principal's data without hitting the server. queryClient.clear(); setUser(null); setErrorState(null); }, []); // The 401 interceptor clears storage outside React; mirror it here so context // state never outlives the stored session while the redirect commits. useEffect(() => { const onSessionCleared = () => setUser(null); window.addEventListener(AUTH_SESSION_CLEARED_EVENT, onSessionCleared); return () => window.removeEventListener(AUTH_SESSION_CLEARED_EVENT, onSessionCleared); }, []); const setError = useCallback((message: string | null) => { setErrorState(message); setIsLoading(false); }, []); const value = useMemo( () => ({ user, isAuthenticated: isTokenValid(user?.token), isLoading, error, login, logout, setError, setLoading: setIsLoading, }), [user, isLoading, error, login, logout, setError], ); return {children}; }