How can I automatically generate a refresh token in the background when the access token is expired and update the localStorage with new one?
I am building a React authentication app and I want the application to silently send a request to the server in the background to update the access token with the refresh token stored in localStorage. Currently, I am using a custom hook called useRefreshToken to add the token in headers and send the request to the server, but I have to call this hook explicitly. I want to know how I can integrate this functionality into the useApiPrivate hook so that it automatically checks and fetches the refresh token, and updates the localStorage without the user having to call the hook explicitly.
Here's the useRefreshToken code:
export default function useRefreshToken() {
const { auth, setAuth } = useAuth();
const refresh = async () => {
try {
const response = await api.get("/newToken", {
// headers:{
// Authorization: `Bearer ${auth.accessToken}`
// }
});
setAuth((prev) => {
console.log(JSON.stringify(prev));
return { ...prev, accessToken: response.data.accessToken };
});
return response.data.accessToken;
} catch (error) {
throw new Error();
}
};
return refresh;
}
Here's the usePrivate code:
export default function useApiPrivate() {
const refresh = useRefreshToken();
const { auth } = useAuth();
console.log(auth.token);
// The useEffect hook is used to add interceptors to the private API instance when the component mounts.
// The auth and refresh variables are dependencies passed to the useEffect hook to ensure that it is only re-run when these values change.
useEffect(() => {
// This line adds an interceptor to the apiPrivate instance which modifies the request configuration before sending it to the server.
// This interceptor checks if the Authorization header is already present and adds the user's token to it if it's not.
const requestIntercept = apiPrivate.interceptors.request.use(
(config) => {
if (!config.headers["Authorization"]) {
config.headers["Authorization"] = `Bearer ${auth?.token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// This line adds another interceptor to the apiPrivate instance which handles unauthorized errors.
// This interceptor checks for a 403 response status and sets a sent flag to prevent multiple refresh requests from being sent.
// It then refreshes the user's token and resends the original request with the new token.
const responseIntercept = apiPrivate.interceptors.use(
(response) => response,
async (error) => {
const prevRequest = error?.config;
if (!error?.response?.status === 403 && !prevRequest?.sent) {
prevRequest.sent = true;
const newAccessToken = await refresh(prevRequest); // pass request config to refresh function
prevRequest.headers["Authorization"] = `Bearer ${newAccessToken}`; // modify request config with new token
return apiPrivate(prevRequest);
}To automatically refresh the access token in the background, you can modify the useApiPrivate hook to include a new useEffect hook that checks if the access token is expired and, if so, fetches a new access token using the refresh token and updates the localStorage.
Here's an updated useApiPrivate code with the new useEffect hook:
javascriptexport default function useApiPrivate() {
const refresh = useRefreshToken();
const { auth } = useAuth();
useEffect(() => {
const checkTokenExpiration = async () => {
const tokenExpiration = auth?.expiration;
if (tokenExpiration && Date.now() > tokenExpiration) {
try {
const newToken = await refresh();
const updatedAuth = { ...auth, token: newToken };
localStorage.setItem("auth", JSON.stringify(updatedAuth));
} catch (error) {
console.log(error);
}
}
};
checkTokenExpiration();
}, [auth, refresh]);
// rest of the code...
}
This new useEffect hook will run on component mount and whenever the auth or refresh dependencies change. It checks if the access token in the auth object has expired by comparing its expiration timestamp with the current time. If the access token has expired, it calls the refresh function to fetch a new access token and updates the auth object in localStorage. Note that you will need to add an expiration field to the auth object to keep track of the token expiration time.
Comments
Post a Comment