fix: solve #5513 — back off /api/auth/token mints on a broken session
Root cause
JwtApiAuthProvider.getJwt() (host-service) mints a JWT by exchanging the session token at /api/auth/token. On failure it simply threw without caching anything, and there was no in-flight coalescing. Because the cloud tRPC client calls getHeaders() → getJwt() on every request (and invalidateCache() on 401), a broken session turned every request into a fresh network mint with no backoff. A wedged multi-workspace client can generate hundreds of POST /api/auth/token calls per session (the issue reports ~600, ~300× the healthy baseline).
That storm is what tripped Vercel's per-IP rate mitigation (x-vercel-mitigated: deny), which then 403'd the login endpoint behind the same blanket rule — a self-locking loop where the human couldn't re-authenticate to clear the broken state.
This PR addresses item #1 from the issue (client-side backoff / circuit breaker). Items #2 (surfacing x-vercel-mitigated: deny as a real UI error) and #3 (server-side firewall scoping of /api/auth/desktop/connect) are out of scope here.
The fix
In JwtApiAuthProvider:
- Circuit breaker — after a failed mint, refuse to touch the network again until an exponentially growing cooldown elapses (
1s → 2s → 4s … capped at 5 min). While the cooldown is activegetJwt()rejects immediately with the last error instead of re-hitting/api/auth/token. A success resets the backoff. - In-flight coalescing — concurrent callers on a cold cache now share a single mint instead of fanning out N token exchanges.
This collapses a broken-session burst from hundreds of requests to a slow trickle, well under any rate-based mitigation.
Tests
JwtAuthProvider.test.ts:
- Reproduction/resolution — 50 sequential failing
getHeaders()calls now issue ≤3 network fetches (was 50, one per call). - Coalescing — 10 concurrent failing calls share a single in-flight fetch (was 10).
- All 8 pre-existing auth-provider tests still pass; typecheck and lint clean.
Closes #5513
Summary by cubic
Add a circuit breaker and in-flight coalescing to JwtApiAuthProvider to stop repeated /api/auth/token mints when a session is broken. Addresses #5513 by backing off failed mints to avoid rate-mitigated lockouts.
- Bug Fixes
- Circuit breaker with exponential backoff (1s → 2s → 4s … capped at 5 min); during cooldown
getJwt()fails fast with the last error; success resets. - Coalesces concurrent
getJwt()calls so only one network mint runs on a cold cache. - Tests: 50 sequential failures perform ≤3 fetches; 10 concurrent failures share 1 fetch; existing tests still pass.
- Circuit breaker with exponential backoff (1s → 2s → 4s … capped at 5 min); during cooldown