In my React Native application, I get an access token and a refresh token after login.
There is a common function to call the access token again using the refresh token when the access token is invalid
On my home page, there are 6 APIs, but when the access token gets invalid these 6 APIs parallelly call to the common function and refresh the access token. This is making that specific user get blocked.
How to prevent this? blocking the rest of the APIs with a flag when the first API’s token becomes invalid is not an option because, before the first response, all the APIs will have refreshed the access token.
The handleResponse function gets triggered first then the code reaches getNewToken funtions then it reaches the dispatch(logout()); because the call for accesstoken does not become success and the app crashes because logout is called multiple times.
const handleResponse = async (
res: ApiResponse<BaseResponseProp, BaseResponseProp> | null,
) => {
if (res?.data?.error === 'invalid_token') {
let tokenVal;
try {
if (token.refresh_token) {
tokenVal = await getNewToken(token.refresh_token);
} else {
//refresh the public token
tokenVal = await refreshPublicToken();
}
if (tokenVal?.access_token && tokenVal?.access_token.length > 0) {
const newTokenHeader = {
Authorization: `Bearer ${tokenVal.access_token}`,
};
invoke({
requestHeader: { ...requestHeader, ...newTokenHeader },
requestBody,
requestParams,
requestQuery,
url: formattedEndpoint,
onResultCallback,
onErrorCallback,
});
dispatch(setToken(tokenVal));
} else {
setInvalidToken(true);
navigation.dispatch(
StackActions.replace(NAVIGATION_SCREENS.ROOT),
);
return;
}
return;
} catch (e) {
const newToken = await refreshPublicToken();
dispatch(setToken(newToken));
return;
}
}
const err = new CustomError(
res?.data?.error || res?.originalError?.message,
res?.data?.status || 200,
);
if (typeof onErrorCallback === 'function') {
onErrorCallback(err);
} else if (typeof onError === 'function') {
onError(err as CustomError);
}
handleParseError(err);
} else {
const responseData = res?.data as unknown as R;
if (renderData) {
setData(res?.data);
}
if (typeof onResultCallback === 'function') {
onResultCallback(res?.data as unknown as R);
} else if (typeof onResult === 'function') {
onResult(res?.data);
}
}
};
const getNewToken = async (refToken: string) => {
let formattedEndpoint = ENDPOINT.LOGIN;
let tokenVal = {};
const header = { refresh_token: refToken };
const requestQuery = {
...REFRESH_AUTH_HEADERS,
...header,
};
const queryParams = new URLSearchParams({
...requestQuery,
}).toString();
formattedEndpoint = `${formattedEndpoint}?${queryParams}`;
const refreshResponse = await api.post<LoginSuccess>(
formattedEndpoint,
{},
{
headers: basicAuthHeaders,
},
);
if (refreshResponse.status === 200) {
tokenVal = {
access_token: refreshResponse.data?.access_token,
refresh_token: refreshResponse.data?.refresh_token,
expires_in: refreshResponse.data?.expires_in,
type: 'private',
};
} else {
// The logout gets called multiple times and app crashes
dispatch(logout());
tokenVal = {
access_token: null,
token_type: null,
refresh_token: null,
expires_in: null,
scope: null,
id_token: null,
type: 'public',
};
tokenVal = await refreshPublicToken();
navigation.dispatch(StackActions.replace(NAVIGATION_SCREENS.ROOT));
}
return tokenVal;
};




