Web Locks API может предотвратить ситуацию, когда пять открытых вкладок одновременно «бомбардируют» сервер аутентификации запросами на обновление токена (refresh-token), спасая пользователей от внезапного выхода из системы. Координируя обновление между вкладками, один запрос заменяет поток запросов, который обычно приводит к завершению сессии при использовании Refresh Token Rotation.

Скрытая перегрузка в многовкладочной сессии

Типичное одностраничное приложение (SPA) добавляет перехватчик (interceptor) Axios, который отслеживает ответ 401, меняет логический флаг isRefreshing и ставит в очередь все исходящие запросы до тех пор, пока не придет новый JWT. При тестировании в одной вкладке этот процесс работает безупречно.

Откройте то же приложение в пяти вкладках, дождитесь истечения срока действия access-токена, и все пять вкладок заметят ошибку 401 в одну и ту же миллисекунду. Каждая вкладка решит, что ей нужно обновить токен, и пять идентичных запросов на обновление (refresh-token) одновременно устремятся к серверу аутентификации. При использовании Refresh Token Rotation — меры безопасности, которая аннулирует предыдущий refresh-токен сразу после выдачи нового — сервер воспримет второй запрос как атаку повторного воспроизведения (replay attack), пометит сессию как скомпрометированную и аннулирует её. Пользователь мгновенно разлогинивается во всех вкладках.

Первопричина заключается в модели изоляции JavaScript. Переменная, такая как isRefreshing, существует только в той вкладке, которая её установила; другие вкладки никак не могут узнать, что процесс обновления уже запущен. Результатом становится классическая проблема конкурентности (concurrency), но «процессами» здесь выступают вкладки браузера, а не потоки.

Почему блокировка между вкладками — правильный инструмент

Нам нужен способ, позволяющий вкладкам взаимодействовать друг с другом по поводу общего ресурса — в данном случае, свежего JWT. Web Locks API, доступный через navigator.locks, предоставляет именно это. Он позволяет скриптам запрашивать именованную блокировку, которую браузер обеспечивает во всех контекстах, принадлежащих одному и тому же источнику (origin). Если блокировка уже занята, другие вызывающие функции ставятся в очередь, пока владелец не освободит её или пока браузер не прервет её (например, при сбое вкладки). Никаких внешних серверов, никакого опроса (polling) — только нативная координация на уровне браузера.

Реализация процесса обновления на основе блокировок

  1. Обнаружение 401 — Перехватчик Axios ловит ответ с ошибкой авторизации, как и прежде.
  2. Запрос эксклюзивной блокировки — Вкладка вызывает navigator.locks.request('auth_token_refresh_lock', async lock => { … }). В callback-функцию может войти только одна вкладка в конкретный момент времени.
  3. Повторная проверка перед сетевым запросом — Внутри блокировки прочитайте временную метку (или сам токен) из localStorage. Если метка была обновлена менее нескольких секунд назад, значит, другая вкладка уже обновила токен; текущая вкладка пропускает сетевой вызов и просто считывает новый JWT из хранилища.
  4. Обновление при необходимости — Если сохраненная метка устарела, отправьте запрос на обновление, сохраните новый токен и текущее время в localStorage, а затем освободите блокировку, завершив выполнение callback-функции.
  5. Возобновление запросов в очереди — Все остальные вкладки, которые ожидали, получают блокировку одну за другой, видят свежую метку и завершают работу, не выполняя повторных запросов.
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());
  });
}

Этот паттерн гарантирует, что, независимо от количества открытых вкладок, до сервера дойдет только один запрос на обновление.

Измеримые преимущества

  • Эффективность сети — Один запрос заменяет пять, что значительно снижает нагрузку на канал связи и сервер.
  • Безопасность сессии — Благодаря Refresh Token Rotation сервер видит только одно использование старого refresh-токена, поэтому он никогда не пометит сессию как скомпрометированную.
  • Отказоустойчивость — Если вкладка, удерживающая блокировку, аварийно завершит работу, браузер автоматически освободит её, предотвращая взаимную блокировку (deadlock), которая в противном случае остановила бы работу всех вкладок.
  • Масштабируемость — Пользователи могут открывать десятки вкладок, не рискуя вызвать каскадный выход из системы, так как координация происходит внутри браузера.

Обратная сторона: поддержка браузерами и способы обхода

Web Locks API — относительно новая функция. Современные браузеры на базе Chromium и последние версии Firefox поддерживают её, но в старых браузерах и Safari нативной поддержки нет. В средах, где API недоступен, разработчикам приходится использовать менее надежные методы — например, трансляцию пользовательского события через localStorage или использование Shared Worker — для имитации межвкладочного взаимодействия. У таких обходных путей нет автоматической защиты от взаимных блокировок, которую обеспечивает navigator.locks, поэтому их следует использовать с осторожностью.

Что изучить дальше

  • Прогресс стандартизации – Следите за кривой внедрения API; более широкая поддержка сделает подход на основе блокировок стандартом для любой координации между вкладками.
  • Обертки библиотек – Несколько утилит с открытым исходным кодом уже абстрагируют паттерн запроса блокировки, что упрощает их интеграцию в существующие интерцепторы Axios.
  • Аудит безопасности – Хотя блокировка решает проблему конкурентного доступа, эндпоинт обновления (refresh endpoint) по-прежнему должен обеспечивать правильную ротацию токенов и ограничение частоты запросов (rate limiting), так как одна вредоносная вкладка все еще может наводнить сервер запросами, если блокировку удастся обойти.

Вывод прост: рассматривайте набор открытых вкладок как распределенную систему и предоставьте им нативный примитив синхронизации. Интегрируя Web Locks API в процесс обновления токена, разработчики избавляются от «ловушки токенов в браузере» и позволяют пользователям оставаться в системе, сколько бы вкладок они ни использовали одновременно.