08

Coalesce Token Refresh and Normalize Failures

Let one runtime refresh while every waiter rechecks the authoritative vault version. • Lab status: conceptual reconstruction. Receipts are expected simulated outputs, not deployed-system measurements.

System map · Day 08

Whole-system design

Five stable layers. Today's work is expanded and linked; the rest stays in context.

Entry and policy

Covered — Client entry · Browser and callback

Onboarding and control

Covered — Connector control plane

Authorization services

Authorization server

Design target · not proved

Receives one upstream refresh for concurrent callers and returns a typed success or failure.

Compute and execution

Covered — External resource server

Connector runtime compute

Design target · not proved

Detects stale authority, joins the shared refresh flight, and retries only after durable renewal.

Refresh coordinator compute

Design target · not proved

Combines local singleflight with a distributed lease and completion signal across processes.

Storage and evidence

Covered — Specification draft store · Connector configuration store · Sanitized discovery cacheAhead — Ordered migration log

Secret and token vault

Design target · not proved

Is rechecked before and after the lease and persists any rotated refresh token before release.

Evidence plane

Source-backed today

Proves one token version for all callers and maps invalid grants to bounded reauthentication.

Traversed today

token · Authorization serverSecret and token vaultsecret resolution · Secret and token vaultConnector runtime computeobserve · Connector runtime computeEvidence plane

Overview

Why an expiring token becomes a distributed-systems problem

Day 07 proved that a runtime can resolve and inject one credential safely. With several runtimes, the same near-expiry token can trigger concurrent refresh requests. If refresh-token rotation is enabled, the first request may invalidate the credential used by the rest, producing a storm or false reauthentication.

Today you will combine in-process singleflight with a distributed lease and completion signal, then normalize provider failures without copying sensitive prose. Refresh-token handling follows the base lifecycle in OAuth 2.0 and the rotation/replay guidance in RFC 9700.

Elect one refresher and make waiters recheck

Singleflight coalesces callers in one process. A distributed lease with a fencing version elects one refresher across processes. The winner rereads the vault, refreshes only if the version is still stale, atomically persists the rotated result, and signals waiters. Every waiter rereads the vault after the signal; it never trusts token material in a message.

const lease = await coordinator.tryAcquire({ key: grantId, ttlMs: 10_000 });
if (!lease.acquired) {
  await coordinator.waitForVersion(grantId, currentVersion + 1);
  return vault.read(grantId);
}

const latest = await vault.read(grantId);
if (latest.version !== currentVersion && isFreshAndUsable(latest)) return latest;
if (latest.version !== currentVersion) throw new Error("ambiguous_grant_state");
return refreshAndPersistAtomically(latest, lease.fence);

{
  "grantId": "grant-d04",
  "versionBefore": 7,
  "versionAfter": 8,
  "refreshCalls": 1,
  "waitingCallers": 11,
  "tokenMaterialInSignal": false
}

Map provider failures to stable gateway actions

Provider strings vary and may contain sensitive detail. The gateway maps a bounded set of protocol facts to stable categories: retryable transport failure, throttled, invalid grant requiring reauthentication, invalid client requiring operator repair, and permanent policy denial. Unknown errors stop safely with a correlation ID.

{
  "providerError": "invalid_grant",
  "gatewayCategory": "reauth_required",
  "retryAutomatically": false,
  "secretFieldsPersisted": false
}

{
  "receipt": "SCG-R08",
  "callers": 12,
  "refreshRequests": 1,
  "committedVersions": [8],
  "normalizedFailure": "reauth_required",
  "unaffectedControl": "other-grant-version-3"
}

The failure path kills the lease holder after contacting the provider but before commit. If the provider might have rotated the refresh token, the vault is now stale and rereading it cannot prove recovery; the next holder must stop, reconcile with provider-supported state when available, or require a fresh Day 04 authorization transaction. A fence prevents a stale process from overwriting a newer local version, but it cannot undo an upstream rotation. Another grant remains the unaffected control. Cleanup expires test leases, removes signals, and destroys disposable grant versions.

Score one recovery action

Given lease state, vault versions, and one normalized provider category, choose only retry, reauthorize, or stop. A transient transport failure within budget may retry; invalid_grant means reauthorize; stale fencing or ambiguous state means stop and reconcile. The evidence is one action plus the decisive supplied fact.

The misconception is “a lock means the token is current.” Replay a waiter after notification and show why the vault recheck is authoritative. Decline arbitrary provider-text classification, unbounded retries, or sending token material through coordination messages.

Carry the token ledger forward

SCG-R08 records caller count, one refresh, fence/version transition, waiter rechecks, normalized errors, recovery, unaffected grant, and cleanup. Day 09 consumes that ordered credential state when mirroring writes and deciding whether a migration may cut over.