import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react' import type { AuthProvider, UserIdentity, LoginParams } from '../types' export interface AuthContextValue { /** Whether the user is authenticated */ isAuthenticated: boolean /** Current user identity */ identity?: UserIdentity /** User permissions */ permissions?: string[] /** Whether auth is being checked */ isLoading: boolean /** Login function */ login: (params: LoginParams) => Promise /** Logout function */ logout: () => Promise /** Refresh auth state */ refreshAuth: () => Promise } export const AuthContext = createContext(null) export interface AuthProviderProps { children: ReactNode authProvider: AuthProvider } /** * Provides authentication state and methods to child components. * * @example * ```tsx * const myAuthProvider: AuthProvider = { * login: async ({ username, password }) => { ... }, * logout: async () => { ... }, * checkAuth: async () => { ... }, * checkError: async (error) => { ... }, * getIdentity: async () => ({ id: '1', fullName: 'John' }), * getPermissions: async () => ['admin'], * } * * * * * ``` */ export function AuthProvider({ children, authProvider }: AuthProviderProps) { const [isAuthenticated, setIsAuthenticated] = useState(false) const [identity, setIdentity] = useState() const [permissions, setPermissions] = useState() const [isLoading, setIsLoading] = useState(true) const refreshAuth = useCallback(async () => { setIsLoading(true) try { await authProvider.checkAuth() const [newIdentity, newPermissions] = await Promise.all([ authProvider.getIdentity(), authProvider.getPermissions(), ]) setIdentity(newIdentity) setPermissions(newPermissions) setIsAuthenticated(true) } catch { setIsAuthenticated(false) setIdentity(undefined) setPermissions(undefined) } finally { setIsLoading(false) } }, [authProvider]) useEffect(() => { refreshAuth() }, [refreshAuth]) const login = useCallback(async (params: LoginParams) => { await authProvider.login(params) await refreshAuth() }, [authProvider, refreshAuth]) const logout = useCallback(async () => { await authProvider.logout() setIsAuthenticated(false) setIdentity(undefined) setPermissions(undefined) }, [authProvider]) return ( {children} ) } /** * Hook to access authentication state and methods. * Must be used within an AuthProvider. * * @example * ```tsx * const { isAuthenticated, identity, login, logout } = useAuth() * * if (!isAuthenticated) { * return * } * ``` */ export function useAuth(): AuthContextValue { const context = useContext(AuthContext) if (!context) { throw new Error('useAuth must be used within an AuthProvider') } return context }