auth.provider API
    Preparing search index...

    Interface RefreshTokenFamilyStore

    Storage primitive for refresh-token families.

    Theme A: this interface exposes only single-key atomic primitives. The 4-outcome rotation ceremony (rotated | replayed | revoked | unknown_family) is composed in the wrapper layer (RefreshTokenFamilyRotation), NOT classified by the adapter.

    Per A3 §5.1.

    interface RefreshTokenFamilyStore {
        kind: string;
        findFamily(familyId: string): Promise<RefreshTokenFamily | null>;
        registerFamily(family: RefreshTokenFamily): Promise<void>;
        updateFamily(
            familyId: string,
            updater: (current: RefreshTokenFamily) => RefreshTokenFamily | null,
        ): Promise<RefreshTokenFamilyUpdateResult>;
    }
    Index

    Properties

    kind: string

    Methods

    • Atomically register a new refresh-token family.

      MUST throw RefreshTokenStorageError({ reason: "duplicate-family" }) if a family with the same familyId already exists (regardless of revoke / TTL state — duplicate familyId indicates RNG collision or programming bug).

      MUST throw RefreshTokenStorageError({ reason: "expired-at-issue" }) if family.expiresAtMs <= now() at call time.

      Concurrency contract: N concurrent calls with the same familyId MUST result in exactly one success and N-1 throws of "duplicate-family".

      Per A3 §5.1.

      Parameters

      Returns Promise<void>

    • Atomically read-modify-write the family aggregate.

      Adapter performs:

      1. Read current family state (or null if non-existent).
      2. Invoke updater(current).
      3. If updater returns a new RefreshTokenFamily, attempt atomic CAS commit using an adapter-internal version token (NOT exposed in RefreshTokenFamily — adapters may use a separate version field, ETag, Redis WATCH, or any backend-native CAS primitive).
      4. On CAS conflict, retry by re-reading state and re-invoking updater up to a bounded retry limit; on exhaustion, throws RefreshTokenStorageError({ reason: "conflict-exhausted" }).
      5. If updater returns null, abort without state change (no retry).

      Updater contract (NORMATIVE):

      • Updater is invoked with the current RefreshTokenFamily value (NEVER null — when the family does not exist, the adapter returns { outcome: "not-found" } directly without invoking the updater).
      • Updater MUST be a pure function (no observable side effects, no async I/O). Adapter MAY invoke updater multiple times due to CAS retry; consumers MUST NOT rely on exactly-once invocation.
      • Updater MUST NOT mutate the input RefreshTokenFamily (it is readonly at the type level; runtime adapters MAY freeze it additionally as defence-in-depth).
      • Updater MUST return either a new RefreshTokenFamily (commit) or null (abort).
      • Updater MAY use a closure-captured variable to communicate the abort reason to the caller; the closure is reset at the top of each updater invocation. This is the wrapper pattern used by createRefreshTokenFamilyRotation to translate "aborted" results into "replayed" or "revoked" outcomes.
      • Updater MUST NOT return a RefreshTokenFamily whose expiresAtMs is <= now(). Both adapters fail-closed by throwing RefreshTokenStorageError({ reason: "expired-at-issue" }) — symmetric with registerFamily and prevents committing a dead-on-arrival entry. Callers shrinking TTL during rotation should compute the new expiresAtMs from a forward window.

      Return value:

      • { outcome: "committed", family } — CAS succeeded; family is the newly-persisted state.
      • { outcome: "not-found" } — family did not exist; updater not invoked.
      • { outcome: "aborted" } — updater returned null; no state change.

      Per A3 §5.1.

      Parameters

      Returns Promise<RefreshTokenFamilyUpdateResult>