Skip to content

v2.9.2

Choose a tag to compare

@zoltanam zoltanam released this 16 May 00:04

Auth refresh-race + relay 401 split-brain fix (re-ship of v2.9.1)

v2.9.2 contains the same code fix as v2.9.1 (which failed to publish — its publish.yml was missing the optional provider SDKs needed by the test gate).

What's fixed

Two compounding bugs were logging users out repeatedly — on CLI restart and mid-session — even when the refresh_token was still valid:

  1. Refresh race in AuthService.refresh_token. Concurrent 401-retries presented the same refresh_token to /api/oauth/refresh. First rotated it; second hit a revoked token and got 400 invalid_grant. Blunt non-200 handler killed the session. Fix: per-process asyncio.Lock + disk re-read inside the lock to dedup concurrent refreshes, plus _classify_refresh_failure so only 400 invalid_grant / 401 / 403 marks the session dead (429 / 5xx / network errors stay transient).
  2. Local 1h clock killed the UI. AuthToken.is_authenticated gated on time.time() >= expires_at → CLI flagged "not connected" after 1h without ever firing the call that would have triggered refresh. Relaxed: server is the source of truth via the 401-retry path.
  3. Relay split-brain. RelayListener owns its own httpx.AsyncClient and bypassed APIClient's refresh-on-401, so the heartbeat fired session_expired instantly on a stale access_token while the login screen correctly reported "logged in". New _authed_request helper threads through heartbeat / mercure-token / command-result and refreshes-and-retries once before declaring the session expired.

Workflow fix (the reason v2.9.2 exists)

publish.yml now installs [test,hetzner,ovh,mcp] to match ci.yml. Without this, the optional-SDK-using tests block every release-triggered publish.

Coordination

Backend agreed to add server-side row locking on the refresh-token row + a short idempotency window in TokenService::refreshToken as belt-and-braces (covers cross-process and shared-auth.json-over-Dropbox cases).

Tests

7 new auth tests + 2 new relay tests. Full suite 2913 passing.