I'm trying to implement refresh-token from react. I'm using this library axios-auth-refresh which seems to work very fine except for one API.
// api.js
import Axios from "axios";
import Cookies from 'js-cookie'
import { TOKEN_COOKIE_NAME, REFRESH_TOKEN_COOKIE_NAME } from '../constants/constants';
import createAuthRefreshInterceptor from 'axios-auth-refresh';
const api = Axios.create({
baseURL: process.env.REACT_APP_BACKEND_URL,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
const refreshAuthLogic = async (failedRequest) => {
const refresh_token = Cookies.get(REFRESH_TOKEN_COOKIE_NAME);
// if(!refresh_token) return;
console.log(refresh_token);
const tokenRefreshResponse = await api.post('auth/createtoken', {
}, {
headers: {'Authorization': 'Bearer ' + refresh_token},
validateStatus: () => true
});
console.log(tokenRefreshResponse);
if(tokenRefreshResponse.data.statusCode === 401 || tokenRefreshResponse.data.statusCode === 403) {
Cookies.remove(REFRESH_TOKEN_COOKIE_NAME);
if(!window.location.href.includes('login')) {
window.location.href = "http://localhost:3000/login";
}
return;
}
const access_token = tokenRefreshResponse.data.access_token;
Cookies.set(TOKEN_COOKIE_NAME, access_token, { expires: 60 })
api.defaults.headers.Authorization = `Bearer ${access_token}`
failedRequest.response.config.headers['Authorization'] = 'Bearer ' + access_token;
}
// Instantiate the interceptor (you can chain it as it returns the axios instance)
createAuthRefreshInterceptor(api, refreshAuthLogic);
export default api;
The following api call does NOT repeat in case 401 is returned:
const fetchUsers = async () => {
const { data } = await api.get(`users/`, {params: {tripUsers: true}}, {
validateStatus: (status) => status !== 401 && status !== 403
})
setUsers(data);
}
useEffect(() => {
fetchUsers();
}, [])
The following api call DOES repeat in case 401 is returned:
const fetchProfile = async () => {
const { data } = await api.get(`/users/${user.userId}`, {}, {
validateStatus: (status) => status !== 401 && status !== 403
})
const {statusCode, message} = data;
console.log(data);
if(!statusCode) {
console.log(data);
setState(data);
}
}
useEffect(() => {
fetchProfile();
}, [])
Please help.
After spending some time on this issue, I decided to create a generic API caller rather than using axios interceptors or any other library. Here's my generic axios API caller. It can still be improved, but the idea is to call the API again with a new token if the first token is expired.
// api.js
import Axios from "axios";
import Cookies from 'js-cookie'
import { TOKEN_COOKIE_NAME, REFRESH_TOKEN_COOKIE_NAME } from '../constants/constants';
const api = Axios.create({
baseURL: process.env.REACT_APP_BACKEND_URL,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
export const callApi = async (method, url, params, other) => {
const validateStatus =
url === 'auth/login' ? () => true : (status) => status !== 401 && status !== 403
const options = {
url,
method,
validateStatus,
...other
}
options[method === 'GET' ? 'params' : 'data'] = params;
console.log(options);
try {
const data = await api(options);
return Promise.resolve(data);
} catch (err) {
console.log(err.response.status);
if (err && err.response && err.response.status === 401) {
return performTokenRefresh(options);
} else {
return Promise.reject(err);
}
}
};
const performTokenRefresh = async (options) => {
const refresh_token = Cookies.get(REFRESH_TOKEN_COOKIE_NAME);
if(!refresh_token) return {};
const tokenRefreshResponse = await api.post('auth/createtoken', {
}, {
headers: {'Authorization': 'Bearer ' + refresh_token},
validateStatus: () => true
});
if(tokenRefreshResponse.data.statusCode === 401 || tokenRefreshResponse.data.statusCode === 403) {
Cookies.remove(REFRESH_TOKEN_COOKIE_NAME);
if(!window.location.href.includes('login')) {
window.location.href = "http://localhost:3000/login";
}
return {};
}
const access_token = tokenRefreshResponse.data.access_token;
Cookies.set(TOKEN_COOKIE_NAME, access_token, { expires: 60 })
api.defaults.headers.Authorization = `Bearer ${access_token}`
return api(options);
}
export default api;