Когда одна сессия открыта в нескольких вкладках, frontend, SPA, SSR-клиенты и SDK начинают делить auth state, storage и кэш. Частая ошибка - защищать refresh token только in-memory single-flight: между вкладками он не работает.
Refresh token как критическая секция
Если 5 вкладок одновременно получили
401, они могут отправить несколько refresh-запросов одним token, получить invalid_grant и перетереть свежие токены старыми. Web Locks API дает mutex на один origin:type Tokens = {
accessToken: string;
refreshToken: string;
};
const LOCK = 'auth:refresh-token';
async function ensureAccessToken(): Promise<string> {
return navigator.locks.request(LOCK, async () => {
const latest: Tokens = await loadTokens();
if (!isExpiring(latest)) {
return latest.accessToken;
}
const next = await refreshTokens(latest.refreshToken);
await saveTokens(next);
return next.accessToken;
});
}Важный паттерн
После входа в lock нужно перечитать storage. Пока вкладка ждала, другая уже могла обновить токены. Практический совет: внутри критической секции всегда делайте double-check precondition, а не запускайте refresh сразу после ожидания.
Где еще полезно
Web Locks хорошо ложится на миграции
IndexedDB/localStorage, одноразовую инициализацию кэша, защиту записи в общий browser storage и координацию фоновых задач между вкладками.Ограничения
Lock работает только в пределах origin. Это не distributed lock и не замена серверной идемпотентности. Критическая секция должна быть короткой; для сетевого refresh ставьте timeout. Если
navigator.locks нет, нужен fallback через BroadcastChannel, storage lease или серверную защиту.Вывод:
Web Locks API полезен там, где несколько вкладок делят runtime-состояние: он снижает риск гонок, но не отменяет серверные гарантии.
