Files
group-2-threaded-project-pe…/web/context/AuthContext.js
augmentedpotato 4bd98ef06f Improve auth flows
2026-04-03 14:52:32 -06:00

124 lines
2.9 KiB
JavaScript

"use client";
import { createContext, useContext, useState, useEffect, useCallback } from "react";
const AuthContext = createContext(null);
const TOKEN_KEY = "auth_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();
}
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [token, setToken] = useState(null);
const [loading, setLoading] = useState(true);
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]);
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]);
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 }),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.message || "Login failed");
}
const jwt = data.token;
localStorage.setItem(TOKEN_KEY, jwt);
setToken(jwt);
const userInfo = await refreshUser(jwt);
return userInfo;
}, [refreshUser]);
const register = useCallback(async ({ username, password, email, fullName, phone }) => {
const res = await fetch("/api/v1/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password, email, fullName, phone }),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.message || "Registration failed");
}
const jwt = data.token;
localStorage.setItem(TOKEN_KEY, jwt);
setToken(jwt);
const userInfo = await refreshUser(jwt);
return userInfo;
}, [refreshUser]);
const logout = useCallback(() => {
localStorage.removeItem(TOKEN_KEY);
setToken(null);
setUser(null);}, []);
return (
<AuthContext.Provider value={{ user, token, loading, login, logout, register, refreshUser }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) {
throw new Error("useAuth must be used within an AuthProvider");
}
return ctx;
}