RM / src /context /AuthContext.tsx
trretretret's picture
Initial commit: Add research assistant application
b708f13
"use client";
import * as React from "react";
import { useRouter } from "next/navigation";
interface User {
email: string;
fullName: string;
isPremium: boolean;
}
interface AuthContextType {
user: User | null;
token: string | null; // Exposed directly for API Client (File #38)
isLoading: boolean;
logout: () => void;
refreshAuth: () => void;
}
const AuthContext = React.createContext<AuthContextType | undefined>(undefined);
/**
* Global Auth Provider
* Centralizes JWT management and reactive token state.
*/
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = React.useState<User | null>(null);
const [token, setToken] = React.useState<string | null>(null);
const [isLoading, setIsLoading] = React.useState(true);
const router = useRouter();
// 1. Memoized Token Decoder
const decodeToken = React.useCallback((jwt: string): User | null => {
try {
const payload = JSON.parse(window.atob(jwt.split(".")[1]));
return {
email: payload.sub,
fullName: payload.full_name || payload.sub.split('@')[0],
isPremium: payload.is_premium === true,
};
} catch {
return null;
}
}, []);
// 2. The "Sync" Engine
const refreshAuth = React.useCallback(() => {
// Only run in browser
if (typeof window === "undefined") return;
const storedToken = localStorage.getItem("token");
if (storedToken) {
const decodedUser = decodeToken(storedToken);
if (decodedUser) {
setUser(decodedUser);
setToken(storedToken);
} else {
// Token is malformed - clear everything
localStorage.removeItem("token");
setUser(null);
setToken(null);
}
} else {
setUser(null);
setToken(null);
}
setIsLoading(false);
}, [decodeToken]);
// Initial Sync
React.useEffect(() => {
refreshAuth();
}, [refreshAuth]);
// 3. Centralized Logout
const logout = React.useCallback(() => {
localStorage.removeItem("token");
setToken(null);
setUser(null);
router.push("/login");
}, [router]);
// 4. Provider Value
const value = React.useMemo(() => ({
user,
token,
isLoading,
logout,
refreshAuth
}), [user, token, isLoading, logout, refreshAuth]);
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = React.useContext(AuthContext);
if (context === undefined) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
}