import { createContext, useContext, useEffect, useState, useCallback } from 'react' import { api } from '../api/client.js' const AuthContext = createContext(null) export function AuthProvider({ children }) { const [user, setUser] = useState(null) const [loading, setLoading] = useState(true) const refresh = useCallback(async () => { try { const data = await api.me() setUser(data.user) } catch { setUser(null) } finally { setLoading(false) } }, []) useEffect(() => { refresh() }, [refresh]) // Step 1. Returns { user } on success, or { totpRequired, challenge } when the // account has 2FA on (caller then calls loginTotp). `extra` carries honeypot. const login = useCallback(async (username, password, extra) => { const data = await api.login(username, password, extra) if (data.user) setUser(data.user) return data }, []) // Public self-registration (player). Creates the account, sets the session // cookie, and returns { user }. `extra` carries the honeypot + optional email. const register = useCallback(async (username, password, extra) => { const data = await api.register(username, password, extra) if (data.user) setUser(data.user) return data }, []) // Step 2 for TOTP users: exchange the challenge + code for a real session. const loginTotp = useCallback(async (challenge, code) => { const data = await api.loginTotp(challenge, code) setUser(data.user) return data.user }, []) // Step 2 for SSO logins whose account has 2FA on. The pending challenge lives in // an httpOnly cookie, so only the code is sent. Returns { user, returnTo }. const ssoLoginTotp = useCallback(async (code) => { const data = await api.ssoLoginTotp(code) setUser(data.user) return data }, []) const logout = useCallback(async () => { try { await api.logout() } finally { setUser(null) } }, []) return ( {children} ) } export function useAuth() { const ctx = useContext(AuthContext) if (!ctx) throw new Error('useAuth must be used within AuthProvider') return ctx }