I have written a small application using React Native and TS. In my application I use authorization through access_tokens with lifetime for one hour. Every time the token expires, I launch refreshToken function where I send access_token and refresh_token that were saved previously in local storage and send it through POST method.
basically something like this:
if (token_from_storage !== is_expired) {
//assuming token is indeed in storage
return access_token;
} else {
//this Function calls an api with refresh token
const { tokens }: { tokens: ITokens } = await refreshToken(refresh_token);
saveTokens(tokens); //here we save new access_token and refresh_token
}
after refreshToken has finished the new tokens are saved locally (and were updated on DB level) and must be used if you want refresh tokens again.
For example, we launch an application with an expired token. The methods on the start will be getUserInfo() and getApplicationInfo() that get some important data. They are launched through redux-saga or something similar (async).
//first app screen
useEffect(() => {
dispatch(ACTION_GET_USER_INFO);
dispatch(ACTION_GET_APPLICATION_INFO);
},[])
first method would launch refresh_token about the same time as the second one. The tokens will be rebuilt for the first method and update on the database level. By the time second method calls refreshToken (he was launched at the same time; he also thinks that the token is dead), the refresh_token will be changed on DB level and would not answer 200 on the refreshToken() call.
What should be done to achieve proper refreshing?
If I am understanding your issue correctly it sounds like you are calling two async function that both refresh tokens if the current token has expired.
This indicates you are experiencing a race condition in your useEffect statement, and because the requests handle refreshing their token independently at least one of the request will have an invalid token.
Personally, I would not being handling tokens in the client at all especially with local storage.
If you must however, you can fix the race condition by adding a service layer that handles requests, and tokens singularly. Your dispatch functions will pass through the service layer which can determine the need to refresh a token and if a token is being refreshed already in a synchronous manner before attempting to send the requests.