"use client"; import { createContext, useContext, useState, useEffect, useCallback } from "react"; //Auth context //Stores the logged in user, token, and auth actions const AuthContext = createContext(null); //Key used to save the token in localStorage const TOKEN_KEY = "auth_token"; //Fetches the current user from the backend using the stored token async function fetchCurrentUser(token) { const res = await fetch("/api/v1/auth/me", { headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) return null; return res.json(); } //Provides auth state to all child components export function AuthProvider({ children }) { const [user, setUser] = useState(null); const [token, setToken] = useState(null); const [loading, setLoading] = useState(true); //Re-fetches the user info using the current or a newly provided token const refreshUser = useCallback(async (providedToken) => { const activeToken = providedToken ?? token; if (!activeToken) { setUser(null); return null; } const userInfo = await fetchCurrentUser(activeToken); if (!userInfo) { localStorage.removeItem(TOKEN_KEY); setToken(null); setUser(null); return null; } if (!token) { setToken(activeToken); } setUser(userInfo); return userInfo; }, [token]); //On first load, check if a token was saved and try to restore the session useEffect(() => { const stored = localStorage.getItem(TOKEN_KEY); if (!stored) { setLoading(false); return; } refreshUser(stored) .catch(() => { localStorage.removeItem(TOKEN_KEY); setToken(null); setUser(null); }) .finally(() => setLoading(false)); }, [refreshUser]); //Logs the user in and saves the token const login = useCallback(async (username, password) => { const res = await fetch("/api/v1/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }), }); let data; try { data = await res.json(); } catch { throw new Error("Unable to log in, please try again later."); } if (!res.ok) { throw new Error(data.message || "Unable to log in, please try again later."); } const jwt = data.token; localStorage.setItem(TOKEN_KEY, jwt); setToken(jwt); const userInfo = await refreshUser(jwt); return userInfo; }, [refreshUser]); //Creates a new account and logs the user in right away const register = useCallback(async ({ username, password, email, firstName, lastName, phone }) => { const res = await fetch("/api/v1/auth/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password, email, firstName, lastName, phone }), }); let data; try { data = await res.json(); } catch { throw new Error("Unable to register, please try again later."); } if (!res.ok) { if (data.errors && typeof data.errors === "object") { const fieldErrors = Object.entries(data.errors) .map(([field, msg]) => `${field}: ${msg}`) .join(", "); throw new Error(fieldErrors || data.message || "Unable to register, please try again later."); } throw new Error(data.message || "Unable to register, please try again later."); } const jwt = data.token; localStorage.setItem(TOKEN_KEY, jwt); setToken(jwt); const userInfo = await refreshUser(jwt); return userInfo; }, [refreshUser]); //Clears the token and user from memory and localStorage const logout = useCallback(() => { localStorage.removeItem(TOKEN_KEY); setToken(null); setUser(null);}, []); return ( {children} ); } //Hook to access auth state, must be used inside an AuthProvider export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) { throw new Error("useAuth must be used within an AuthProvider"); } return ctx; }