The Web Locks API can stop five open tabs from simultaneously hammering an auth server with refresh-token calls, saving users from sudden logouts. By coordinating refreshes across tabs, a single request replaces the flood that normally triggers a session-kill when Refresh Token Rotation is in use.

The hidden overload in a multi-tab session

A typical single-page app adds an Axios interceptor that watches for a 401 response, flips a Boolean isRefreshing flag, and queues any outgoing requests until a new JWT arrives. Tested in one tab, the flow works flawlessly.

Open the same app in five tabs, let the access token expire, and all five tabs notice the 401 at the same millisecond. Each tab thinks it must refresh, so five identical refresh-token requests race the auth server. With Refresh Token Rotation—a security measure that invalidates the previous refresh token as soon as a new one is issued—the server sees the second request as a replay attack, flags the session as compromised, and revokes it. The user is logged out of every tab in an instant.

The root cause is JavaScript’s isolation model. A variable such as isRefreshing lives only in the tab that set it; other tabs have no way to know a refresh is already in progress. The result is a classic concurrency problem, but the “processes” are browser tabs rather than threads.

Why a cross-tab lock is the right tool

What we need is a way for tabs to talk to each other about a shared resource—in this case, the fresh JWT. The Web Locks API, exposed as navigator.locks, gives exactly that. It lets scripts request a named lock that the browser enforces across all contexts belonging to the same origin. If a lock is already held, other callers are queued until the holder releases it or the browser aborts it (for example, when the tab crashes). No external server, no polling, just native browser coordination.

Implementing the lock-based refresh flow

  1. Detect the 401 – The Axios interceptor catches the unauthorized response as before.
  2. Ask for an exclusive lock – The tab calls navigator.locks.request('auth_token_refresh_lock', async lock => { … }). Only one tab can enter the callback at a time.
  3. Double-check before hitting the network – Inside the lock, read a timestamp (or the token itself) from localStorage. If the timestamp is less than a few seconds old, another tab has already refreshed the token; the current tab skips the network call and simply reads the new JWT from storage.
  4. Refresh if needed – If the stored timestamp is stale, send the refresh request, store the new token and the current time in localStorage, then release the lock by returning from the callback.
  5. Resume queued requests – All other tabs that were waiting acquire the lock one after another, see the fresh timestamp, and finish without making another request.
async function refreshIfNeeded() {
  await navigator.locks.request('auth_token_refresh_lock', async lock => {
    const lastRefresh = Number(localStorage.getItem('token_refreshed_at') || 0);
    const now = Date.now();
    if (now - lastRefresh < 5_000) return; // another tab already refreshed

    const newToken = await callRefreshEndpoint(); // actual network call
    localStorage.setItem('jwt', newToken);
    localStorage.setItem('token_refreshed_at', now.toString());
  });
}

The pattern guarantees that, regardless of how many tabs are open, only one refresh request reaches the server.

Benefits you can measure

  • Network efficiency – One request replaces five, cutting bandwidth and server load dramatically.
  • Session safety – With Refresh Token Rotation, the server sees only a single use of the old refresh token, so it never flags the session as compromised.
  • Resilience – If the tab holding the lock crashes, the browser automatically releases the lock, preventing a deadlock that would otherwise stall all tabs.
  • Scalability – Users can open dozens of tabs without risking a cascade logout, because the coordination stays within the browser.

The flip side: browser support and fallbacks

The Web Locks API is a relatively new feature. Modern Chromium-based browsers and recent versions of Firefox implement it, but older browsers and Safari lack native support. In environments where the API is unavailable, developers must fall back to a less reliable technique—such as broadcasting a custom event via localStorage or using a shared worker—to approximate cross-tab signaling. Those workarounds lack the automatic dead-lock protection that navigator.locks provides, so they should be used with caution.

What to watch next

  • 標準化の進展 – APIの採用曲線に注目してください。サポートが拡大すれば、タブ間連携におけるロックベースのアプローチがデフォルトになるでしょう。
  • ライブラリのラッパー – いくつかのオープンソースのユーティリティが、すでにロックリクエストのパターンを抽象化しており、既存のAxiosインターセプターへの組み込みが容易になっています。
  • セキュリティ監査 – ロックによって並行性の問題は解決されますが、リフレッシュエンドポイントでは引き続き適切なトークンローテーションとレート制限を強制する必要があります。なぜなら、ロックがバイパスされた場合、単一の悪意のあるタブがサーバーに大量のリクエストを送りつける可能性があるからです。

結論はシンプルです。開いているタブの集合を分散システムとして扱い、ネイティブの同期プリミティブを提供することです。Web Locks APIをトークンリフレッシュのフローに組み込むことで、開発者は「ブラウザタブのトークントラップ」を排除し、ユーザーがいくつのタブを同時に開いていても、ログイン状態を維持できるようになります。