// ============================================================ // ZCLAW Admin V2 — Auth Guard with session restore // ============================================================ // // Auth strategy: // 1. If Zustand has token (normal flow after login) → authenticated // 2. If no token but account in localStorage → call GET /auth/me // to validate HttpOnly cookie and restore session // 3. If cookie invalid → clean up and redirect to /login import { useEffect, useRef, useState } from 'react' import { Navigate, useLocation } from 'react-router-dom' import { Spin } from 'antd' import { useAuthStore } from '@/stores/authStore' import { authService } from '@/services/auth' export function AuthGuard({ children }: { children: React.ReactNode }) { const token = useAuthStore((s) => s.token) const account = useAuthStore((s) => s.account) const login = useAuthStore((s) => s.login) const logout = useAuthStore((s) => s.logout) const location = useLocation() // Track restore attempt to avoid double-calling const restoreAttempted = useRef(false) const [restoring, setRestoring] = useState(false) useEffect(() => { if (restoreAttempted.current) return restoreAttempted.current = true // If no in-memory token but account exists in localStorage, // try to validate the HttpOnly cookie via /auth/me if (!token && account) { setRestoring(true) authService.me() .then((meAccount) => { // Cookie is valid — restore session // Use sentinel token since real auth is via HttpOnly cookie login('cookie-session', '', meAccount) setRestoring(false) }) .catch(() => { // Cookie expired or invalid — clean up stale data logout() setRestoring(false) }) } }, []) // eslint-disable-line react-hooks/exhaustive-deps if (restoring) { return (
) } if (!token) { return } return <>{children} }