From 91800574e4228194b5769cb413a96861d7e56ae4 Mon Sep 17 00:00:00 2001 From: Mattrixwv Date: Sun, 2 Mar 2025 22:41:43 -0500 Subject: [PATCH] Fix token refresh issues --- src/pages/public/LoginPage.tsx | 27 +++++++++++++++---------- src/providers/AuthProvider.tsx | 36 ++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/pages/public/LoginPage.tsx b/src/pages/public/LoginPage.tsx index cb4d98a..92f7c4e 100644 --- a/src/pages/public/LoginPage.tsx +++ b/src/pages/public/LoginPage.tsx @@ -2,12 +2,14 @@ import PrimaryButton from "@/components/button/PrimaryButton"; import PasswordInput from "@/components/input/PasswordInput"; import TextInput from "@/components/input/TextInput"; import { useAuth } from "@/providers/AuthProvider"; +import { useTimedModal } from "@/providers/TimedModalProvider"; import { Navigate, useNavigate } from "react-router"; export default function LoginPage(){ const { jwt, setJwt } = useAuth(); const navigate = useNavigate(); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); const login = async (formData: FormData) => { @@ -15,22 +17,27 @@ export default function LoginPage(){ const password = formData.get("password") as string; - const response = await fetch(`${import.meta.env.VITE_API_URL}/auth/token`, { + await fetch(`${import.meta.env.VITE_API_URL}/auth/token`, { method: "GET", headers: { Authorization: "Basic " + btoa(`${username}:${password}`) }, credentials: "include" + }) + .then(async (response) => { + if(response.status === 200){ + addSuccessMessage("Logged in successfully"); + const json = await response.json(); + setJwt(json.token); + navigate("/raidGroup"); + } + else{ + addErrorMessage("Failed to log in: " + ((await response.json()).errors as string[]).join(",\n")); + } + }) + .catch(error => { + addErrorMessage("Failed to log in: " + error); }); - - if(response.status === 200){ - const json = await response.json(); - setJwt(json.token); - navigate("/raidGroup"); - } - else{ - //TODO: Handle error - } } diff --git a/src/providers/AuthProvider.tsx b/src/providers/AuthProvider.tsx index 8d2d9ca..411d83a 100644 --- a/src/providers/AuthProvider.tsx +++ b/src/providers/AuthProvider.tsx @@ -30,39 +30,50 @@ export function AuthProvider({ children }: AuthProviderProps){ const [ jwt, setJwt ] = useState(null); - const [ expiration, setExpiration ] = useState(null) + const [ expiration, setExpiration ] = useState(null); + const [ firstFetch, setFirstFetch ] = useState(true); const fetchToken = useCallback(async () => { + console.log("Fetching token"); try{ const response = await api.get("/auth/refresh"); //If the token is retrieved if((response.status === 200) && (!response.data.errors)){ setJwt(response.data.token); - setExpiration(new Date(atob(response.data.token.split(".")[1]))); + setExpiration(new Date(JSON.parse(atob(response.data.token.split(".")[1])).exp * 1000)); + setFirstFetch(false); + return response.data.token; } //If the token cannot be retrieved else{ setJwt(null); setExpiration(null); + setFirstFetch(false); } } //If the token cannot be retrieved catch{ setJwt(null); setExpiration(null); + setFirstFetch(false); } - }, [ setJwt, setExpiration ]); + }, [ setJwt, setExpiration, setFirstFetch ]); //Add the token to all queries useLayoutEffect(() => { - if((expiration) && (expiration < new Date())){ - fetchToken(); - } + const authInterceptor = api.interceptors.request.use(async (config) => { + if(config.url?.endsWith("/auth/refresh")){ + return config; + } + let currentJwt = jwt; + if((expiration) && (expiration < new Date()) && (!config.url?.endsWith("/auth/refresh"))){ + currentJwt = await fetchToken(); + config.headers.Authorization = jwt ? `Bearer ${currentJwt}` : config.headers.Authorization; + } + config.headers.Authorization = jwt ? `Bearer ${currentJwt}` : config.headers.Authorization; - const authInterceptor = api.interceptors.request.use(config => { - config.headers.Authorization = jwt ? `Bearer ${jwt}` : config.headers.Authorization; return config; }); @@ -83,6 +94,15 @@ export function AuthProvider({ }), [ jwt, setJwt, expiration, setExpiration ]); //TODO: Return a spinner while the first token is being fetched + if(firstFetch){ + return ( +
+ Loading... +
+ ); + } return (