I have a code where I try to make a real world simulation. for this example I want to simulate that I want to make a web request if and only if there is a token in localstorage with the key "token". The problem is that it executes the amount of web requests that I have running at the moment.
const getData = async () => {
const data = await instance.get("todos/1");
setData(data.data);
await instance.get("todos/2");
await instance.get("todos/3");
};
.
.
.
instance.interceptors.request.use(async (req) => {
token = localStorage.getItem("token") || null;
console.log(req.url);
if (!token) {
console.log("not exist token");
//cancel request because token not exists
return req;
} else {
console.log("token exist");
req.headers.Authorization = `Bearer ${token}`;
return req;
}
});
My idea is to cancel or not execute the web requests when there is no token in localstorage, and if there is, I would like to send the token in the headers.
How can I do it?
this is my live code
There's a api for request cancellation in axios
https://github.com/axios/axios#cancellation
const cancelSource = axios.CancelToken.source();
const instance = axios.create({
cancelToken: cancelSource.token,
timeout: 1000 * 10,
});
instance.interceptors.request.use((req) => {
if (condition) {
cancelSource.cancel('No Authorization Token')
}
return req
})
BUT, There's much simpler way to abort request.
Just throw an error in the interceptor.
instance.interceptors.request.use((req) => {
if (condition) {
throw new Error('No Authorization Token')
}
return req
})
Either of two ways throws Promise Error.
I recommend the latter one, Throwing error.
Related
I've encountered a very strange problem, implementing axios interceptors for handling the expired token and refreshing it.
Setting
I'm implementing the JWT authentication with access and refresh tokens.
When the request is being sent to the API route that requires JWT authentication, request interceptor is here to make sure the headers contain an Authorization with Bearer token. The response interceptor checks if the new access token is needed, sends a request to refresh it, and finally updates the axios instance with the new config.
I wrote the code following the Dave Gray's video, but with TypeScript.
Problem
When testing this code, I set the refresh token lifetime to be very long, while setting the access token lifetime to be 5 seconds. After it expires, when the request to the protected route is happening, everything goes according to the plan—the logs from the backend contain two successfully completed requests: (1) to the protected route with 401 response and then (2) the refresh request.
At this point, I see the DOMException in the browser console (Chrome and Safari), which states that setRequestHeader fails to execute because a source code function is not a valid header value. Which, of course, it is not! The piece of code is this.
Code
const axiosPrivate = axios.create({
baseURL: BASE_URL,
headers: { "Content-Type": "application/json" },
withCredentials: true,
});
interface IRequestConfig extends AxiosRequestConfig {
sent?: boolean;
}
const useAxiosPrivate = () => {
const { auth } = useAuth()!;
const refresh = useRefreshToken();
React.useEffect(() => {
const requestInterceptor = axiosPrivate.interceptors.request.use(
(config: AxiosRequestConfig) => {
config.headers = config.headers ?? {};
if (!config.headers["Authorization"]) {
config.headers["Authorization"] = `Bearer ${auth?.token}`;
}
return config;
},
async (error: AxiosError): Promise<AxiosError> => {
return Promise.reject(error);
}
);
const responseInterceptor = axiosPrivate.interceptors.response.use(
(response: AxiosResponse) => response,
async (error: AxiosError): Promise<AxiosError> => {
const prevRequestConfig = error.config as IRequestConfig;
if (error?.response?.status === 401 && !prevRequestConfig?.sent) {
const newAccessToken = await refresh();
prevRequestConfig.sent = true;
prevRequestConfig.headers = prevRequestConfig.headers!;
prevRequestConfig.headers[
"Authorization"
] = `Bearer ${newAccessToken}`;
return axiosPrivate(prevRequestConfig);
}
return Promise.reject(error);
}
);
return () => {
axiosPrivate.interceptors.request.eject(requestInterceptor);
axiosPrivate.interceptors.response.eject(responseInterceptor);
};
}, [auth, refresh]);
return axiosPrivate;
};
Error
DOMException: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'function (header, parser) {
header = normalizeHeader(header);
if (!header) return undefined;
const key = findKey(this, header);
if (key) {
const value = this[key];
if (!parser) {
return value;
}
if (parser === true) {
return parseTokens(value);
}
if (_utils_js__WEBPACK_IMPORTED_MODULE_0__["default"].isFunction(parser)) {
return parser.call(this, value, key);
}
if (_utils_js__WEBPACK_IMPORTED_MODULE_0__["default"].isRegExp(parser)) {
return parser.exec(value);
}
throw new TypeError('parser must be boolean|regexp|function');
}
}' is not a valid HTTP header field value.
Research
So far, I've only found one similar issue in the internet, which has links to some others. One of them gives me a hint, that it may be the problem with how axios reads the configuration given to an axios instance.
I'm not sure if the problem is indeed somewhere in axios. I'll be extremely grateful for any useful thoughts on this problem!
I had the same problem, I solved it by manually giving value to axiosPrivate instead of axiosPrivate(prevRequestConfig).
const responseIntercept = axiosPrivate.interceptors.response.use(
response => response,
async (error)=>{
const prevRequest = error?.config;
if (error?.response?.status === 403 && !prevRequest?.sent){
const newAccessToken = await refresh();
// console.log(prevRequest);
return axiosPrivate({
...prevRequest,
headers: {...prevRequest.headers, Authorization: `Bearer ${newAccessToken}`},
sent: true
});
}
return Promise.reject(error);
}
);
Thanks to Daniel Dan's solution I could modify Dave's tutorial code:
const responseInterceptor = axiosPrivate.interceptors.response.use(
(response: AxiosResponse) => {
return response;
},
async (error: AxiosError): Promise<AxiosError> => {
const prevRequestConfig = error.config as AxiosRequestConfig;
if (error?.response?.status === 401 && !prevRequestConfig.sent) {
prevRequestConfig.sent = true;
const newAccessToken = await refresh();
/* --- The modified line --- */
prevRequestConfig.headers = { ...prevRequestConfig.headers };
/* ------------------------- */
prevRequestConfig.headers[
"Authorization"
] = `Bearer ${newAccessToken}`;
return axiosPrivate(prevRequestConfig);
}
return Promise.reject(error);
}
);
Just Do This in your response interceptor
const responseInterceptor = axiosPrivate.interceptors.response.use(
(response: AxiosResponse) => response,
async (error: AxiosError): Promise<AxiosError> => {
const prevRequestConfig = error.config as IRequestConfig;
if (error?.response?.status === 401 && !prevRequestConfig?.sent) {
const newAccessToken = await refresh();
prevRequestConfig.sent = true;
prevRequestConfig.headers["Authorization"] = `Bearer ${newAccessToken}`;
return axiosPrivate({
...prevRequestConfig,
...{
headers: prevRequestConfig.headers.toJSON(),
},
});
}
return Promise.reject(error);
}
);
When re-sending the request with updated creds, i.e axiosPrivate(config), the headers property needs to be a plain javascript Object but instead it is converted internally to be an AxiosInstance object.
To fix it, just pass a plain Javascript object to the headers property of your prevRequestConfig object.
So here is my issue.
I am using JWT authentication in my project and i have an axiosInstance setup in my react project . I also have an interceptor for axiosInstance which takes care of intercepting and refreshing tokens when required.
const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 360000,
transformRequest: [
function (data, headers) {
const accessToken = window.localStorage.getItem('access_token');
if (accessToken) {
headers['Authorization'] = `Bearer ${accessToken}`;
} else {
delete headers.Authorization;
}
return JSON.stringify(data);
},
],
headers: {
'Content-Type': 'application/json',
accept: 'application/json',
},
});
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async function (error) {
const originalRequest = error.config;
console.log(
'Caught the error response. Here is your request ',
originalRequest,
);
// case 1: No error specified Most likely to be server error
if (typeof error.response === 'undefined') {
// Uncomment this later
alert('Server error occured');
return Promise.reject(error);
}
// case 2: Tried to refresh the token but it is expired. So ask user to login again
if (
error.response.status === 401 &&
originalRequest.url === baseURL + 'auth/api/token/refresh/'
) {
store.dispatch(setLoginFalse());
return Promise.reject(error);
}
// Case 3: Got 401 Unauthorized error. There are different possiblities
console.log('Error message in axios = ', error.response.data);
if (
error.response.status === 401 &&
error.response.statusText === 'Unauthorized'
) {
const refreshToken = localStorage.getItem('refresh_token');
console.log('Refresh token = ', refreshToken);
// See if refresh token exists
// Some times undefined gets written in place of refresh token.
// To avoid that we check if refreshToken !== "undefined". This bug is still unknown need to do more research on this
if (refreshToken !== undefined && refreshToken !== 'undefined') {
console.log(typeof refreshToken == 'undefined');
console.log('Refresh token is present = ', refreshToken);
const tokenParts = JSON.parse(atob(refreshToken.split('.')[1]));
// exp date in token is expressed in seconds, while now() returns milliseconds:
const now = Math.ceil(Date.now() / 1000);
console.log(tokenParts.exp);
// Case 3.a Refresh token is present and it is not expired - use it to get new access token
if (tokenParts.exp > now) {
return axiosInstance
.post('auth/api/token/refresh/', { refresh: refreshToken })
.then((response) => {
localStorage.setItem('access_token', response.data.access);
axiosInstance.defaults.headers['Authorization'] =
'Bearer ' + response.data.access;
originalRequest.headers['Authorization'] =
'Bearer ' + response.data.access;
console.log('access token updated');
// After refreshing the token request again user's previous url
// which was blocked due to unauthorized error
// I am not sure by default axios performs get request
// But since we are passing the entire config of previous request
// It seems to perform same request method as previous
return axiosInstance(originalRequest);
})
.catch((err) => {
// If any error occurs at this point we cannot guess what it is
// So just console log it
console.log(err);
});
} else {
// Refresh token is expired ask user to login again.
console.log('Refresh token is expired', tokenParts.exp, now);
store.dispatch(setLoginFalse());
}
} else {
// refresh token is not present in local storage so ask user to login again
console.log('Refresh token not available.');
store.dispatch(setLoginFalse());
}
}
// specific error handling done elsewhere
return Promise.reject(error);
},
);
export default axiosInstance;
Note that i have Content-Type set as 'application/json' in axiosIntance.
But my problem is inorder to upload images the content type should be 'multipart/form-data --boundary: set-automatically'.
(NOTE: Manually setting boundary for multipart data doesn't seem to work)
The boundary for multipart data is set automatically by axios if we don't put the content-type in header. But for that i have to somehow delete the content-type from axiosInstance at one place (from where i am uploading the image) without disturbing axiosInstance used at other parts of the project.
I tested it with fetch and by setting up new axios instance it works as expected. But the problem is these requests won't be intercepted by axios for refreshing JWT tokens if needed to.
I read various posts on this, but i still don't see a way to solve this.
I cann provide any more details if required. Please help me, i already spent 8+ hours debugging this.
Thank you.
Edit 1
I changed the handleSubmit function to this
const handleSubmit = (e) => {
e.preventDefault();
console.log(file);
let formData = new FormData();
formData.append('profile_pic', file);
formData.append('name', 'root');
axiosInstance.defaults.headers.common['Content-Type'] =
'multipart/form-data';
axiosInstance
.put('/users/profile-pic-upload/', formData)
.then((res) => console.log(res))
.catch((err) => console.log(err));
};
But the content type is still application/json
But Let's say i changed the content-type in core axios.js to 'multipart/form-data' it changes the content type of all requests. It will break other things but as expected it won't fix this issue. Because setting manual boundary doesn't seems to work. Even this post says to remove the content type during multipart data so that it is handled automatically by library (axios in this case)
For passing anything dynamic to your axios instance, use a function that returns the axios instance like this:
import axios from 'axios';
const customAxios = (contentType) => {
// axios instance for making requests
const axiosInstance = axios.create({
// your other properties for axios instance
headers: {
'Content-Type': contentType,
},
});
// your response interceptor
axiosInstance.interceptors.response.use(// handle response);
return axiosInstance;
};
export default customAxios;
And now, you can use axios like:
import customAxios from './customAxios';
const axiosForJSON = customAxios('application/json');
const axiosForMultipart = customAxios('multipart/form-data');
axiosForJSON.get('/hello');
axiosForMultipart.post('/hello', {});
// OR
cusomAxios('application/json').get('/hello');
axiosInstance.defaults.headers.put['Content-Type'] = "multipart/form-data";
Or
axiosInstance.interceptors.request.use(config => {
config.headers.put['Content-Type'] = 'multipart/form-data';
return config;
});
Try this for your specific instance.
Answer above from Lovlesh Pokra helped me.
In my case of checking the access token when downloading a file - response needs to be parsed for the new access token. However since this interceptor is within the class that is used to download the file with the responseType set to arrayBuffer while creation
responseType: 'arraybuffer'
I had to change the responseType to json like below
youraxiosinstance.defaults.responseType = "json";
and then setting it back to arraybuffer - so file download can continue
youraxiosinstance.defaults.responseType = "arraybuffer";
based upon your need - just before the call - the change can be done as required by you.
I have seen axios documentation, but all it says is
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
Also many tutorials only show this code but I am confused what it is used for, can someone please give me simple example to follow.
To talk in simple terms, it is more of a checkpoint for every HTTP action. Every API call that has been made, is passed through this interceptor.
So, why two interceptors?
An API call is made up of two halves, a request, and a response. Since it behaves like a checkpoint, the request and the response have separate interceptors.
Some request interceptor use cases -
Assume you want to check before making a request if your credentials are valid. So, instead of actually making an API call, you can check at the interceptor level that your credentials are valid.
Assume you need to attach a token to every request made, instead of duplicating the token addition logic at every Axios call, you can make an interceptor that attaches a token on every request that is made.
Some response interceptor use cases -
Assume you got a response, and judging by the API responses you want to deduce that the user is logged in. So, in the response interceptor, you can initialize a class that handles the user logged in state and update it accordingly on the response object you received.
Assume you have requested some API with valid API credentials, but you do not have the valid role to access the data. So, you can trigger an alert from the response interceptor saying that the user is not allowed. This way you'll be saved from the unauthorized API error handling that you would have to perform on every Axios request that you made.
Here are some code examples
The request interceptor
One can print the configuration object of axios (if need be) by doing (in this case, by checking the environment variable):
const DEBUG = process.env.NODE_ENV === "development";
axios.interceptors.request.use((config) => {
/** In dev, intercepts request and logs it into console for dev */
if (DEBUG) { console.info("✉️ ", config); }
return config;
}, (error) => {
if (DEBUG) { console.error("✉️ ", error); }
return Promise.reject(error);
});
If one wants to check what headers are being passed/add any more generic headers, it is available in the config.headers object. For example:
axios.interceptors.request.use((config) => {
config.headers.genericKey = "someGenericValue";
return config;
}, (error) => {
return Promise.reject(error);
});
In case it's a GET request, the query parameters being sent can be found in config.params object.
The response interceptor
You can even optionally parse the API response at the interceptor level and pass the parsed response down instead of the original response. It might save you the time of writing the parsing logic again and again in case the API is used in the same way in multiple places. One way to do that is by passing an extra parameter in the api-request and use the same parameter in the response interceptor to perform your action. For example:
//Assume we pass an extra parameter "parse: true"
axios.get("/city-list", { parse: true });
Once, in the response interceptor, we can use it like:
axios.interceptors.response.use((response) => {
if (response.config.parse) {
//perform the manipulation here and change the response object
}
return response;
}, (error) => {
return Promise.reject(error.message);
});
So, in this case, whenever there is a parse object in response.config, the manipulation is done, for the rest of the cases, it'll work as-is.
You can even view the arriving HTTP codes and then make the decision. For example:
axios.interceptors.response.use((response) => {
if(response.status === 401) {
alert("You are not authorized");
}
return response;
}, (error) => {
if (error.response && error.response.data) {
return Promise.reject(error.response.data);
}
return Promise.reject(error.message);
});
You can use this code for example, if you want to catch the time that takes from the moment that the request was sent until the moment you received the response:
const axios = require("axios");
(async () => {
axios.interceptors.request.use(
function (req) {
req.time = { startTime: new Date() };
return req;
},
(err) => {
return Promise.reject(err);
}
);
axios.interceptors.response.use(
function (res) {
res.config.time.endTime = new Date();
res.duration =
res.config.time.endTime - res.config.time.startTime;
return res;
},
(err) => {
return Promise.reject(err);
}
);
axios
.get("http://localhost:3000")
.then((res) => {
console.log(res.duration)
})
.catch((err) => {
console.log(err);
});
})();
It is like a middle-ware, basically it is added on any request (be it GET, POST, PUT, DELETE) or on any response (the response you get from the server).
It is often used for cases where authorisation is involved.
Have a look at this: Axios interceptors and asynchronous login
Here is another article about this, with a different example: https://medium.com/#danielalvidrez/handling-error-responses-with-grace-b6fd3c5886f0
So the gist of one of the examples is that you could use interceptor to detect if your authorisation token is expired ( if you get 403 for example ) and to redirect the page.
I will give you more practical use-case which I used in my real world projects. I usually use, request interceptor for token related staff (accessToken, refreshToken), e.g., whether token is not expired, if so, then update it with refreshToken and hold all other calls until it resolves. But what I like most is axios response interceptors where you can put your apps global error handling logic like below:
httpClient.interceptors.response.use(
(response: AxiosResponse) => {
// Any status code that lie within the range of 2xx cause this function to trigger
return response.data;
},
(err: AxiosError) => {
// Any status codes that falls outside the range of 2xx cause this function to trigger
const status = err.response?.status || 500;
// we can handle global errors here
switch (status) {
// authentication (token related issues)
case 401: {
return Promise.reject(new APIError(err.message, 409));
}
// forbidden (permission related issues)
case 403: {
return Promise.reject(new APIError(err.message, 409));
}
// bad request
case 400: {
return Promise.reject(new APIError(err.message, 400));
}
// not found
case 404: {
return Promise.reject(new APIError(err.message, 404));
}
// conflict
case 409: {
return Promise.reject(new APIError(err.message, 409));
}
// unprocessable
case 422: {
return Promise.reject(new APIError(err.message, 422));
}
// generic api error (server related) unexpected
default: {
return Promise.reject(new APIError(err.message, 500));
}
}
}
);
How about this. You create a new Axios instance and attach an interceptor to it. Then you can use that interceptor anywhere in your app
export const axiosAuth = axios.create()
//we intercept every requests
axiosAuth.interceptors.request.use(async function(config){
//anything you want to attach to the requests such as token
return config;
}, error => {
return Promise.reject(error)
})
//we intercept every response
axiosAuth.interceptors.request.use(async function(config){
return config;
}, error => {
//check for authentication or anything like that
return Promise.reject(error)
})
Then you use axiosAuth the same way you use axios
This is the way I used to do in my project. The code snippet refers how to use access and refresh token in the axios interceptors and will help to implements refresh token functionalities.
const API_URL =
process.env.NODE_ENV === 'development'
? 'http://localhost:8080/admin/api'
: '/admin-app/admin/api';
const Service = axios.create({
baseURL: API_URL,
headers: {
Accept: 'application/json',
},
});
Service.interceptors.request.use(
config => {
const accessToken = localStorage.getItem('accessToken');
if (accessToken) {
config.headers.common = { Authorization: `Bearer ${accessToken}` };
}
return config;
},
error => {
Promise.reject(error.response || error.message);
}
);
Service.interceptors.response.use(
response => {
return response;
},
error => {
let originalRequest = error.config;
let refreshToken = localStorage.getItem('refreshToken');
const username = EmailDecoder(); // decode email from jwt token subject
if (
refreshToken &&
error.response.status === 403 &&
!originalRequest._retry &&
username
) {
originalRequest._retry = true;
return axios
.post(`${API_URL}/authentication/refresh`, {
refreshToken: refreshToken,
username,
})
.then(res => {
if (res.status === 200) {
localStorage.setItem(
'accessToken',
res.data.accessToken
);
localStorage.setItem(
'refreshToken',
res.data.refreshToken
);
originalRequest.headers[
'Authorization'
] = `Bearer ${res.data.accessToken}`;
return axios(originalRequest);
}
})
.catch(() => {
localStorage.clear();
location.reload();
});
}
return Promise.reject(error.response || error.message);
}
);
export default Service;
I have implemented in the following way
httpConfig.js
import axios from 'axios'
import { baseURL } from '../utils/config'
import { SetupInterceptors } from './SetupInterceptors'
const http = axios.create({
baseURL: baseURL
})
SetupInterceptors(http)
export default http
SetupInterceptors.js
import { baseURL } from '../utils/config'
export const SetupInterceptors = http => {
http.interceptors.request.use(
config => {
config.headers['token'] = `${localStorage.getItem('token')}`
config.headers['content-type'] = 'application/json'
return config
},
error => {
return Promise.reject(error)
}
)
http.interceptors.response.use(function(response) {
return response
}, function (error) {
const status = error?.response?.status || 0
const resBaseURL = error?.response?.config?.baseURL
if (resBaseURL === baseURL && status === 401) {
if (localStorage.getItem('token')) {
localStorage.clear()
window.location.assign('/')
return Promise.reject(error)
} else {
return Promise.reject(error)
}
}
return Promise.reject(error)
})
}
export default SetupInterceptors
Reference : link
so im rather new to axios and context but I have an Auth context that is provided at App level and this context is used by multiple child components. Within this context I have an axios interceptor that checks requests for 401 (unauthorized) and then calls the refresh token api and replaces the token with a new one. My only concern is that the second time the refresh token API is called it goes into an endless loop of calling the refresh token api? Any ideas what im doing wrong? Any help would be greatly appreciated.
AuthContext.js
axios.interceptors.response.use((response) => {
return response
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && originalRequest.url ===
`${BASE_URI}/Identity/Login`) {
history.push('/login');
return Promise.reject(error);
}
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
const localStorage = JSON.parse(sessionStorage.getItem(AUTH_USER))
const refreshToken = localStorage.refreshToken;
return axios.post(`${BASE_URI}/Identity/Refresh`, null,
{
headers: {
'Refresh-Token': refreshToken
}
})
.then(res => {
if (res.status === 201 || res.status === 200) {
console.log("In refresh request !")
console.log(res)
setSession(null, res.data.token, res.data.refreshToken)
axios.defaults.headers.common['authorization'] = 'Bearer ' + res.data.token;
return axios(originalRequest);
}
}).catch((error) => {
console.log("Inside error refresh")
return Promise.reject(error);
})
}
return Promise.reject(error);
});
I have done something similar to get a refresh token when the token expires and I have encountered the same problem, actually, you are using the same instance of Axios, create another instance
const instance = axios.create();
axios.interceptors.request.use(async (config) => {
if (token && refreshToken) {
const data = JSON.parse(atob(token.split('.')[1]));
const time = Math.floor(new Date().getTime() / 1000);
if (data.exp < time) {
instance.defaults.headers.common["Authorization"] = `Bearer ${refreshToken}`;
const { data } = await instance.get(SERVER.API_ROOT + '/tokens/refresh');
if (data?.AccessToken) localStorage.setItem(config.AUTH_TOKEN, data.AccessToken)
else localStorage.clear();
}
return config;
}
Hope the above example will help you
#J.Naude I have done he similar thing but a generic wrapper around axios which i wrote for one of my project that handles almost all the edge cases
https://gist.github.com/tapandave/01960228516dd852a49c74d16c0fddb1
Hey I know this is an old question, but it seems that your problem was using the same axios instance to request a refresh token, essentially creating a nested refresh-token cycle. What you could do is create a new axios instance (alongside with the initial instance, you would use them both) without an interceptor like this: const noInterceptAxios = axios.create();, and then later use it to send requests where you don't need to check the access token, return noInterceptAxios.post(`/Identity/Refresh).then().catch().
I am trying to make a common interceptor to authorize all my outgoing requests.
My logic is as follows:
All my outgoing requests/API calls passes through the interceptor first to check on my token expiry and in-case the response was un-authorized I make an internal call to refresh the token and update my storage keys with the new values.
Now I need to recall the original request with the new token value, but I can't figure out how to detect the original request that passed through the interceptor.
This is my code :
export const unregister = fetchIntercept.register({
request: function (url, config) {
return [url, config];
},
requestError: function (error) {
return Promise.reject(error);
},
responseError: function (error) {
return Promise.reject(error);
},
response: function (response) {
if (response.status == 401) {
Services.refreshToken((res)=>{
if (res.message == 'success') {
// if token has been refreshed
// recall the request again
}else {
// login again
}
})
}else {
return response;
}
}
})
The problem is that I've no idea how to recall the same request that passed through the interceptor.. I did some search on this, but couldn't find a way to execute this.
fetch-intercept doesn't seem to provide the original url on your response, so indeed you cannot retrigger your request.
The axios network library also allows to register a response interceptor, and additionally provides info on the original request url:
axios.interceptors.response.use(undefined, async apiError => {
if (
apiError.response &&
apiError.response.status === 401 &&
apiError.config
) {
try {
// refresh access token
const res = await Services.refreshToken();
if (res.message === 'success') {
const { config: oldRequest } = apiError;
// retrigger old request
axios.request({...oldRequest, headers: {},});
} else {
// go to login page.
}
} catch (error) {
// refresh failed, return to login page
}
}