Fix token refresh issues

This commit is contained in:
2025-03-02 22:41:43 -05:00
parent 7c3b462651
commit 91800574e4
2 changed files with 45 additions and 18 deletions

View File

@@ -2,12 +2,14 @@ import PrimaryButton from "@/components/button/PrimaryButton";
import PasswordInput from "@/components/input/PasswordInput"; import PasswordInput from "@/components/input/PasswordInput";
import TextInput from "@/components/input/TextInput"; import TextInput from "@/components/input/TextInput";
import { useAuth } from "@/providers/AuthProvider"; import { useAuth } from "@/providers/AuthProvider";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { Navigate, useNavigate } from "react-router"; import { Navigate, useNavigate } from "react-router";
export default function LoginPage(){ export default function LoginPage(){
const { jwt, setJwt } = useAuth(); const { jwt, setJwt } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
const { addSuccessMessage, addErrorMessage } = useTimedModal();
const login = async (formData: FormData) => { const login = async (formData: FormData) => {
@@ -15,22 +17,27 @@ export default function LoginPage(){
const password = formData.get("password") as string; 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", method: "GET",
headers: { headers: {
Authorization: "Basic " + btoa(`${username}:${password}`) Authorization: "Basic " + btoa(`${username}:${password}`)
}, },
credentials: "include" 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
}
} }

View File

@@ -30,39 +30,50 @@ export function AuthProvider({
children children
}: AuthProviderProps){ }: AuthProviderProps){
const [ jwt, setJwt ] = useState<string | null>(null); const [ jwt, setJwt ] = useState<string | null>(null);
const [ expiration, setExpiration ] = useState<Date | null>(null) const [ expiration, setExpiration ] = useState<Date | null>(null);
const [ firstFetch, setFirstFetch ] = useState(true);
const fetchToken = useCallback(async () => { const fetchToken = useCallback(async () => {
console.log("Fetching token");
try{ try{
const response = await api.get("/auth/refresh"); const response = await api.get("/auth/refresh");
//If the token is retrieved //If the token is retrieved
if((response.status === 200) && (!response.data.errors)){ if((response.status === 200) && (!response.data.errors)){
setJwt(response.data.token); 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 //If the token cannot be retrieved
else{ else{
setJwt(null); setJwt(null);
setExpiration(null); setExpiration(null);
setFirstFetch(false);
} }
} }
//If the token cannot be retrieved //If the token cannot be retrieved
catch{ catch{
setJwt(null); setJwt(null);
setExpiration(null); setExpiration(null);
setFirstFetch(false);
} }
}, [ setJwt, setExpiration ]); }, [ setJwt, setExpiration, setFirstFetch ]);
//Add the token to all queries //Add the token to all queries
useLayoutEffect(() => { useLayoutEffect(() => {
if((expiration) && (expiration < new Date())){ const authInterceptor = api.interceptors.request.use(async (config) => {
fetchToken(); 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; return config;
}); });
@@ -83,6 +94,15 @@ export function AuthProvider({
}), [ jwt, setJwt, expiration, setExpiration ]); }), [ jwt, setJwt, expiration, setExpiration ]);
//TODO: Return a spinner while the first token is being fetched //TODO: Return a spinner while the first token is being fetched
if(firstFetch){
return (
<main
className="text-4xl"
>
Loading...
</main>
);
}
return ( return (