import { useSyncExternalStore } from "react"; import type { AuthenticatedUser } from "../api/types"; import { queryClient } from "../app/queryClient"; import { useSessionStore } from "../store/sessionStore"; import { rememberSession } from "../shell/host/rememberedSession"; let current: AuthenticatedUser | null = null; let generation = 0; const listeners = new Set<() => void>(); function notify() { for (const listener of listeners) listener(); } function scrubUserBoundState(): void { queryClient.clear(); useSessionStore.getState().resetSession(); } /** Authentication is intentionally process-local; no browser storage is involved. */ export function getAuthState(): AuthenticatedUser | null { return current; } export function setAuthState(user: AuthenticatedUser): void { if (current && (current.issuer !== user.issuer || current.subject !== user.subject)) rememberSession(current, null); scrubUserBoundState(); current = user; generation += 1; notify(); } /** Refresh expiry/CSRF metadata without discarding the current user's work. */ export function refreshAuthState(user: AuthenticatedUser): void { if (!current || current.issuer !== user.issuer || current.subject !== user.subject || current.isAdmin !== user.isAdmin || JSON.stringify([...current.permissions].sort()) !== JSON.stringify([...user.permissions].sort()) || JSON.stringify([...current.roles].sort()) !== JSON.stringify([...user.roles].sort())) { setAuthState(user); return; } current = user; notify(); } export function clearAuthState(): void { if (current) rememberSession(current, null); scrubUserBoundState(); current = null; generation += 1; notify(); } export function isAuthGenerationCurrent(expectedGeneration: number): boolean { return generation === expectedGeneration; } export function clearAuthStateIfCurrent(expectedGeneration: number): boolean { if (!isAuthGenerationCurrent(expectedGeneration)) return false; clearAuthState(); return true; } export function getAuthGeneration(): number { return generation; } export function subscribeAuthState(listener: () => void): () => void { listeners.add(listener); return () => listeners.delete(listener); } export function useAuthState(): AuthenticatedUser | null { return useSyncExternalStore(subscribeAuthState, getAuthState, getAuthState); } export function useAuthUser(): AuthenticatedUser | null { return useAuthState(); } export function useAuthGeneration(): number { return useSyncExternalStore(subscribeAuthState, getAuthGeneration, getAuthGeneration); } export function hasPermission(user: Pick | null | undefined, permission: string): boolean { return user?.permissions.includes(permission) ?? false; }