import { useCallback, useEffect, useState } from "react"; import { fetchAuthStatus, login as loginRequest, logout as logoutRequest, register as registerRequest, type AuthUser, } from "./authClient"; export interface AuthState { /** * Whether this deployment offers login at all (runtime flag, D-015). * Starts false — the auth UI stays hidden until the /me probe confirms the * endpoints exist, so a disabled deployment never flashes a Sign in button. */ enabled: boolean; /** The signed-in user, or null while anonymous (the default). */ user: AuthUser | null; /** Rejects with a UI-safe Error message on failure (e.g. 401). */ login: (username: string, password: string) => Promise; /** Rejects with a UI-safe Error message on failure (400/409). */ register: (username: string, password: string) => Promise; /** Always resolves; local state is cleared even if the server is down. */ logout: () => Promise; } /** * Session state for the M4 auth UI. The session cookie is HttpOnly, so the * only source of truth is GET /api/auth/me, probed once on mount. * `fetchAuthStatus` never rejects — 401 resolves to anonymous, and 404 * (AUTH_ENABLED=false, D-015) or network failure (offline, server down) * resolve to `enabled: false`, keeping the anonymous-first invariant: * a failing /me must not surface any error. */ export function useAuth(): AuthState { const [enabled, setEnabled] = useState(false); const [user, setUser] = useState(null); useEffect(() => { let cancelled = false; fetchAuthStatus().then((status) => { // Guard against StrictMode double-mounts / fast unmounts. if (cancelled) return; setEnabled(status.enabled); if (status.user) setUser(status.user); }); return () => { cancelled = true; }; }, []); const login = useCallback(async (username: string, password: string) => { setUser(await loginRequest(username, password)); }, []); const register = useCallback(async (username: string, password: string) => { // Register signs the user in immediately (the 201 sets the cookie). setUser(await registerRequest(username, password)); }, []); const logout = useCallback(async () => { await logoutRequest(); setUser(null); }, []); return { enabled, user, login, register, logout }; }