Fix token refresh issues
This commit is contained in:
@@ -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){
|
if(response.status === 200){
|
||||||
|
addSuccessMessage("Logged in successfully");
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
setJwt(json.token);
|
setJwt(json.token);
|
||||||
navigate("/raidGroup");
|
navigate("/raidGroup");
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
//TODO: Handle error
|
addErrorMessage("Failed to log in: " + ((await response.json()).errors as string[]).join(",\n"));
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
addErrorMessage("Failed to log in: " + error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
Reference in New Issue
Block a user