Skip to content

Class: RefreshMutex\<T\>

Class: RefreshMutex<T>

Defined in: auth-client/src/core/refresh-mutex.ts:27

Coalesce concurrent refresh attempts.

Without this, a UI that fires N requests just-in-time of access-token expiry would issue N refresh calls — each one rotating the chain, each one invalidating its predecessors. On the auth-server side, that’s the “refresh-token reuse” signal which trips family-revoke (AUDIT 1.9): every concurrent caller after the first sees their request fail AND kills the session.

The mutex pattern:

  • First caller invokes the underlying refresh function and stashes the in-flight promise.
  • Every subsequent caller during the window awaits the same promise — they get the same new token pair, no second network call.
  • When the promise resolves (success or failure), the slot clears so the NEXT refresh window starts fresh.

Cross-tab: this only coalesces within ONE tab. The cross-tab story is handled separately via BroadcastChannel — when tab A refreshes, it broadcasts the new tokens so tab B picks them up without doing its own refresh. The mutex still helps inside each tab for the just-in-time burst.

Type Parameters

T

T

Constructors

Constructor

new RefreshMutex<T>(): RefreshMutex<T>

Returns

RefreshMutex<T>

Methods

pending()

pending(): Promise<T> | null

Defined in: auth-client/src/core/refresh-mutex.ts:57

Used by tests. Returns the current in-flight promise or null.

Returns

Promise<T> | null


run()

run(op): Promise<T>

Defined in: auth-client/src/core/refresh-mutex.ts:40

If a refresh is already in flight, returns the same promise. Otherwise invokes op and stashes its promise until it settles.

Failures clear the slot so the next call starts a fresh attempt. Success clears the slot so the NEXT-after-success call starts fresh too — we don’t cache successful results across calls (the caller persists the new tokens; the mutex is purely about deduplication of concurrent attempts).

Parameters

op

() => Promise<T>

Returns

Promise<T>