How to refresh token, and not fail…

Krzysztof Kruk
DNA Technology
Published in
4 min readOct 25, 2021
Photo by Micah Williams on Unsplash

While implementing OAuth 2.0 authentication flow, you may want to implement a refresh token mechanism to keep your users logged in but still secured. Most common solutions work fine if your application does not perform many asynchronous calls at the same time.

But what if you have a request heavy application? What problems can occur? And how to solve them? I will answer those questions by describing my journey through a ReactJS application with Axios.

Refresh token mechanism

But let’s start from the beginning. How does a full authentication flow look like?

OAuth 2.0 authentication flow

We need to handle the 401 status code in a response in a way that will:

  1. Request new access token based on refresh token that we have. That request should also have grant type set to “refresh_token”.
  2. Repeat the original request with a new access token.

The most common solution for this problem is to use an interceptor that implements this logic.

Here comes the trouble

The solution above works fine if your application performs requests synchronously, one by one. It would still work with many asynchronous calls if your authentication provider keep all refresh tokens valid after generating a new one. In that case, it would generate a lot of unnecessary traffic but it would work.

However, if you need to perform more requests at the same time, and your authentication provider invalidates the refresh token after generating a new one, you will run into a race condition while refreshing theaccess token.

Race condition on the refresh token

I know, this looks a bit messy but let me explain.

  1. Several calls are being dispatched.
  2. The first call gets 401 and an interceptor asks authentication provider to refresh the token.
  3. In the meantime, the second call also got 401 and asks for a new token (which invalidates the previous one!)
  4. The first call is being repeated with an already invalid token — gets 401 again.
  5. Since it was a repeated call and we don’t want to end up in an infinite loop, we log a user out.

Here comes the sun

To solve this problem, we need to build a queue for upcoming requests, which will be blocked if there is a refresh token call pending. Once a refresh token is returned, the queue is released, and all waiting calls will be repeated.

First, let’s extract a logic responsible for refreshing the token and repeating a request to a separate file to make it a bit cleaner:

Next, let’s define an array for requests that should be subscribed for repeat once the refresh token call is done. We can also already pass the callback for repeating the call to this array.

Now we should implement a logic that will manage the tokenBeingRefreshed flag, and will release the requestsWaiting queue once the refresh token call is done.

As you can see in the example above, this way we can not only safely perform a refresh token call without running into a race condition but also can reduce the number of refresh token calls.

Wrap up

Real-world applications perform many asynchronous requests. Handling unauthorized responses is not an easy task in that case. Using a simple interceptor that makes a refresh token call and repeats the request is not enough. In the best case, we end up with huge traffic triggered by unauthorized responses. The worse scenario is when we run into a race condition because each refresh token invalidates the previous one.

It is hard to find a common solution for this problem. The solution I have implemented and explained in this article is to queue requests and make them wait for the refresh token request to be finished.

Do you have a better idea?

--

--