import { useState, useEffect, useRef, type ReactNode } from 'react'; import { AuthContext, type AuthState } from './useAuth'; import { setTokenGetter } from './api-client'; const AUTH_API = '/api/auth'; export default function AuthProvider({ children }: { children: ReactNode }) { const [state, setState] = useState({ isLoading: true, isAuthenticated: false, user: null, error: null, }); const tokenRef = useRef(null); const authStarted = useRef(false); useEffect(() => { // 设置全局 token getter setTokenGetter(() => tokenRef.current); // 防止 StrictMode 双重调用(jumpToken 一次性使用) if (authStarted.current) return; authStarted.current = true; // 监听 401 事件 const onUnauthorized = () => { tokenRef.current = null; sessionStorage.removeItem('bi_jwt'); setState({ isLoading: false, isAuthenticated: false, user: null, error: '会话已过期' }); }; window.addEventListener('auth:unauthorized', onUnauthorized); authenticate(); return () => window.removeEventListener('auth:unauthorized', onUnauthorized); }, []); async function authenticate() { // 1. 检查 sessionStorage 中是否有 JWT const savedToken = sessionStorage.getItem('bi_jwt'); if (savedToken) { tokenRef.current = savedToken; // 验证 token 是否仍然有效(尝试请求 health) try { const res = await fetch('/api/health', { headers: { Authorization: `Bearer ${savedToken}` }, }); if (res.ok) { const savedUser = sessionStorage.getItem('bi_user'); setState({ isLoading: false, isAuthenticated: true, user: savedUser ? JSON.parse(savedUser) : null, error: null, }); return; } } catch { /* token 无效,继续流程 */ } sessionStorage.removeItem('bi_jwt'); sessionStorage.removeItem('bi_user'); } // 2. 从 URL 提取 jumpToken const params = new URLSearchParams(window.location.search); const jumpToken = params.get('jumpToken'); if (!jumpToken) { // 临时:无 token 时直接放行 setState({ isLoading: false, isAuthenticated: true, user: null, error: null }); return; } try { // 3. 一步完成:jumpToken → 用户信息 + JWT const res = await fetch(`${AUTH_API}/exchange?jumpToken=${encodeURIComponent(jumpToken)}`); const data = await res.json(); if (!res.ok || !data.token) { setState({ isLoading: false, isAuthenticated: false, user: null, error: data.message || '跳转令牌无效或已过期' }); return; } // 4. 存储 JWT tokenRef.current = data.token; sessionStorage.setItem('bi_jwt', data.token); sessionStorage.setItem('bi_user', JSON.stringify(data.user)); // 6. 清除 URL 中的 jumpToken params.delete('jumpToken'); const cleanUrl = params.toString() ? `${window.location.pathname}?${params.toString()}${window.location.hash}` : `${window.location.pathname}${window.location.hash}`; window.history.replaceState({}, '', cleanUrl); setState({ isLoading: false, isAuthenticated: true, user: data.user, error: null, }); } catch (e) { setState({ isLoading: false, isAuthenticated: false, user: null, error: '认证过程出错' }); } } return ( {children} ); }