"use client"; import { useCallback, useEffect, useState } from "react"; import { fetchAuthConfig, loginWithRedirect, fetchMe, getValidAccessToken, clearTokens, logout, type AuthConfig, } from "@/lib/auth"; interface AuthUser { user_id: string; email: string | null; username: string | null; roles: string[]; } export function useAuth() { const [config, setConfig] = useState(null); const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [isAuthenticated, setIsAuthenticated] = useState(false); const load = useCallback(async () => { setLoading(true); try { try { const cfg = await fetchAuthConfig(); setConfig(cfg); } catch { // Config blip must not wipe an existing session. } const token = await getValidAccessToken(); if (!token) { setUser(null); setIsAuthenticated(false); return; } try { const me = await fetchMe(token); setUser(me); setIsAuthenticated(true); } catch { // Access may have expired mid-flight — try one refresh path again. const retry = await getValidAccessToken(); if (!retry || retry === token) { clearTokens(); setUser(null); setIsAuthenticated(false); return; } try { const me = await fetchMe(retry); setUser(me); setIsAuthenticated(true); } catch { clearTokens(); setUser(null); setIsAuthenticated(false); } } } finally { setLoading(false); } }, []); useEffect(() => { load(); // Keep session alive while tab is open (proactive refresh). const id = window.setInterval(async () => { const token = await getValidAccessToken(); if (!token) { setUser(null); setIsAuthenticated(false); return; } }, 45_000); const onFocus = async () => { const token = await getValidAccessToken(); if (!token) { setUser(null); setIsAuthenticated(false); } }; const onVis = async () => { if (document.visibilityState !== "visible") return; const token = await getValidAccessToken(); if (!token) { setUser(null); setIsAuthenticated(false); } }; window.addEventListener("focus", onFocus); document.addEventListener("visibilitychange", onVis); return () => { window.clearInterval(id); window.removeEventListener("focus", onFocus); document.removeEventListener("visibilitychange", onVis); }; }, [load]); const login = async (redirectPath?: string) => { await loginWithRedirect(redirectPath); }; const signOut = async () => { const cfg = config || (await fetchAuthConfig()); await logout(cfg); }; return { user, config, loading, isAuthenticated, login, signOut, reload: load }; }