valhalla / frontend /src /hooks /useAuth.jsx
Dontcryx_07
final
d660fa9
Raw History Blame Contribute Delete
2.52 kB
/**
* useAuth — authentication context provider.
*
* Validates a stored token against /api/auth/me on load and exposes
* login/logout backed by the backend auth routes; persists the token in
* localStorage.
*
* Architecture: wraps the whole app in App.jsx; consumed by InfoBar,
* LoginButton, LoginModal, and RosterManager.
*
* Design: one shared context so every auth-gated component reads the same
* session state.
*/
import { createContext, useContext, useState, useEffect, useCallback } from "react";
import { apiUrl } from "../utils/api";
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [token, setToken] = useState(() => localStorage.getItem("valhalla_token"));
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!token) {
setLoading(false);
return;
}
fetch(apiUrl("/api/auth/me"), {
headers: { Authorization: `Bearer ${token}` },
})
.then((r) => (r.ok ? r.json() : Promise.reject()))
.then((u) => setUser(u))
.catch(() => {
localStorage.removeItem("valhalla_token");
setToken(null);
})
.finally(() => setLoading(false));
}, [token]);
const login = useCallback(async (email, password) => {
const res = await fetch(apiUrl("/api/auth/login"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: "Login failed" }));
throw new Error(err.detail || "Login failed");
}
const data = await res.json();
localStorage.setItem("valhalla_token", data.token);
setToken(data.token);
setUser(data.user);
return data;
}, []);
const logout = useCallback(async () => {
if (token) {
await fetch(apiUrl("/api/auth/logout"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
}).catch(() => {});
}
localStorage.removeItem("valhalla_token");
setToken(null);
setUser(null);
}, [token]);
return (
<AuthContext.Provider
value={{ token, user, isAuthenticated: !!user, loading, login, logout }}
>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth must be used within AuthProvider");
return ctx;
}