back to all notes

mobile1 min read

Sharing One Token Refresh Across Parallel Requests in Flutter

I ran into a token refresh race while testing app startup with a poor connection and expired authentication. Startup sends several requests in parallel: user data, remote configuration, telemetry, and notification counts can all be requested at about the same time.

When requests are rejected with 401 Unauthorized, an interceptor that handles each failure independently can send several refresh requests for the same expired token.

Share the refresh request

I used a static Completer inside the interceptor so those requests could wait for one refresh:

  1. On the first 401, check isRefreshing. If it is false, set it to true, create the Completer, and start the refresh call.
  2. If a refresh is already running, wait on completer.future instead of starting another one.
  3. After the refresh succeeds, update secure storage, complete the Completer, reset the flag, and retry the waiting requests with the new token.

A cached Future can serve the same purpose. The shared state needs to cover the requests using the same authentication state; a static variable is not coordination across Dart isolates.

This addresses duplicate refreshes. It does not establish that the native HTTP connection queue was the cause of the startup delay.

What still needs checking

The connection-limit explanation assumed an iOS URLSession limit of six connections per host. That needs verification against the Dio adapter, platform version, session configuration, and HTTP version in use. A connection limit is not necessarily a limit on concurrent HTTP requests.

The success path above also leaves failure handling to the implementation: waiting requests need to be released if refresh fails, the flag must be reset, and retries need a bound so another 401 does not cause an endless refresh loop. Those are checks to make, not behavior demonstrated by this outline.