diff --git a/src/Web/autopilot-monitor-web/contexts/TenantContext.tsx b/src/Web/autopilot-monitor-web/contexts/TenantContext.tsx index 9870c93e..c26ee87b 100644 --- a/src/Web/autopilot-monitor-web/contexts/TenantContext.tsx +++ b/src/Web/autopilot-monitor-web/contexts/TenantContext.tsx @@ -10,19 +10,34 @@ interface TenantContextType { const TenantContext = createContext(undefined); -const LEGACY_FAKE_TENANT_ID = 'deadbeef-dead-beef-dead-beefdeadbeef'; +// Legacy localStorage key — older builds wrote the active tenantId here so it +// survived reloads. The current model treats the JWT `/api/auth/me` response as +// the single source of truth, so we never read from this key anymore. We still +// proactively clear it on mount to remove a stale value that an XSS payload or +// a curious user could otherwise plant — this closes a small mount-window risk +// where a child component could fetch with a tampered tenantId before the auth +// effect overrides it. The defense is belt-and-braces: the backend MUST also +// derive tenantId from the JWT, never from query/body params. +const LEGACY_TENANT_ID_LS_KEY = 'tenantId'; export function TenantProvider({ children }: { children: React.ReactNode }) { const { user, isAuthenticated, isLoading } = useAuth(); - const [tenantId, setTenantId] = useState(() => { - if (typeof window !== 'undefined') { - const stored = localStorage.getItem('tenantId'); - // Migrate away from the old fake placeholder ID - return (stored && stored !== LEGACY_FAKE_TENANT_ID) ? stored : ''; + // Always start empty. Consumers across the app already gate fetches on + // `if (!tenantId) return;` so the empty initial render is the expected path. + const [tenantId, setTenantId] = useState(''); + + // One-shot cleanup of any stale localStorage value left over from older builds. + useEffect(() => { + if (typeof window === 'undefined') return; + try { + if (localStorage.getItem(LEGACY_TENANT_ID_LS_KEY) !== null) { + localStorage.removeItem(LEGACY_TENANT_ID_LS_KEY); + } + } catch { + // localStorage may be disabled (privacy mode) — ignore. } - return ''; - }); + }, []); // Update tenant ID from authenticated user useEffect(() => { @@ -42,13 +57,6 @@ export function TenantProvider({ children }: { children: React.ReactNode }) { } }, [user, isAuthenticated, isLoading, tenantId]); - // Save tenant ID to localStorage - useEffect(() => { - if (typeof window !== 'undefined') { - localStorage.setItem('tenantId', tenantId); - } - }, [tenantId]); - return ( {children}