My refresh scenario works fine, but i only call the interceptor when the user changes so after i login, but when i refresh the page the userRef will be empty and also the interceptor won’t be called
i’ve tried a use effect on mount when the token is available on my local storage but it still didn’t work for me
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import { jwtDecode } from "jwt-decode";
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errorMessage, setErrorMessage] = useState("");
const [successMessage, setSuccessMessage] = useState("");
const userRef = useRef(null);
const navigate = useNavigate();
useEffect(() => {
if (userRef.current) {
setupAxiosInterceptors();
}
}, [userRef.current]);
//Login Handler
const handleLogin = async (e) => {
e.preventDefault();
try {
const response = await axios.post("http://localhost:5000/api/login", {
email,
password,
});
const { accessToken, refreshToken } = response.data;
localStorage.setItem("token", accessToken);
const decodedUser = jwtDecode(accessToken);
userRef.current = { ...decodedUser, refreshToken };
console.log("user refresh Token: ", userRef.current.refreshToken);
setErrorMessage("");
setSuccessMessage("Logged in successfully.");
setTimeout(() => {
navigate("/dashboard");
}, 500);
} catch (error) {
if (
error.response &&
(error.response.status === 400 || error.response.status === 500)
) {
setErrorMessage("Credentials not correct. Please try again.");
setSuccessMessage("");
} else {
console.error("Login error:", error);
setErrorMessage("Failed to log in. Please try again later.");
setSuccessMessage("");
}
}
};
//Generate a new token
const refreshAccessToken = async () => {
try {
if (!userRef.current || !userRef.current.refreshToken) {
throw new Error("Refresh token not available");
}
const response = await axios.post(
"http://localhost:5000/api/refresh-token",
{ token: userRef.current.refreshToken }
);
const newAccessToken = response.data.accessToken;
localStorage.setItem("token", newAccessToken);
console.log("New access token set ... ");
return newAccessToken;
} catch (error) {
console.log(error);
}
};
// axios interceptor
const setupAxiosInterceptors = () => {
axios.interceptors.request.use(
async (config) => {
let token = localStorage.getItem("token");
if (!token) {
throw new Error("Token is not available");
}
const decodedToken = jwtDecode(token);
const tokenExpiry = decodedToken.exp * 1000;
const currentDate = new Date().getTime();
// Compare token expiry with current time
setTimeout(async () => {
if (tokenExpiry - 5000 < currentDate) {
token = await refreshAccessToken();
console.log("New token:", token);
config.headers["Authorization"] = `Bearer ${token}`;
console.log("Token refreshed successfully");
}
}, 1000);
return config;
},
(error) => {
return Promise.reject(error);
}
);
};
return (
<div className="text-center">
<form
onSubmit={handleLogin}
className="flex flex-col gap-4 items-center *:w-[300px] *:rounded-md *:border-2 *:border-solid *:border-green-600 *:indent-2 *:text-black mt-10"
>
<input
type="email"
name="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type="password"
name="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<input
type="submit"
value="Login"
className="bg-red-500 border-none font-bold"
/>
</form>
{errorMessage && <span className="text-red-700">{errorMessage}</span>}
{successMessage && (
<span className="text-green-700">{successMessage}</span>
)}
</div>
);
};
export default Login;