QuickConnect authorize writes the token and the request flag in two unguarded round trips #35

Open
opened 2026-09-26 18:34:05 +10:00 by unkin-agent · 0 comments
Member

Context: multi-replica stateless deployment (two 2-replica apps, cheeztv and fafflix, each one PostgreSQL plus valkey, no sticky sessions). Follow-up from #18 / PR #34. Tracking: #12.

Problem

QuickConnectManager.cs:167-168 calls SetAuthorizationAsync then SetRequestAsync as two separate round trips. A valkey failure between them leaves a minted access token in valkey and a device row in PostgreSQL that the poll leg never reveals — Authenticated stays false for the full TTL — and the never-released claim makes the request unauthorizable on retry.

Same defect class that SetRequestScript was introduced to remove for the two-key request write. Narrow window, bounded blast radius, but it leaks a live credential the user can never reach.

Proposal

Write the authorization and the Authenticated flag in one atomic step, following the existing Lua pattern (SetRequestScript, ClaimAuthorizationScript). Preserve the current client-visible ordering: Authenticated: true must only become observable once the token exists, so a client polling mid-authorize cannot see authenticated and then 404 on exchange.

Acceptance

  • Two independently constructed instances against real PostgreSQL and real valkey: a transport failure injected between the two writes leaves the request either fully authorized or not authorized at all — never a token the poll cannot reveal.
  • Existing ordering guarantee still holds under the raced-authorize test.
  • Tests use PostgreSqlTestServer and RedisTestServer in tests/Jellyfin.Server.Tests and run in the postgres-migration-chain Woodpecker step. In-memory providers have hidden different semantics and are how three bugs reached production.
Context: multi-replica stateless deployment (two 2-replica apps, `cheeztv` and `fafflix`, each one PostgreSQL plus valkey, no sticky sessions). Follow-up from #18 / PR #34. Tracking: #12. ## Problem `QuickConnectManager.cs:167-168` calls `SetAuthorizationAsync` then `SetRequestAsync` as two separate round trips. A valkey failure between them leaves a minted access token in valkey and a device row in PostgreSQL that the poll leg never reveals — `Authenticated` stays false for the full TTL — and the never-released claim makes the request unauthorizable on retry. Same defect class that `SetRequestScript` was introduced to remove for the two-key request write. Narrow window, bounded blast radius, but it leaks a live credential the user can never reach. ## Proposal Write the authorization and the `Authenticated` flag in one atomic step, following the existing Lua pattern (`SetRequestScript`, `ClaimAuthorizationScript`). Preserve the current client-visible ordering: `Authenticated: true` must only become observable once the token exists, so a client polling mid-authorize cannot see authenticated and then 404 on exchange. ## Acceptance - Two independently constructed instances against real PostgreSQL and real valkey: a transport failure injected between the two writes leaves the request either fully authorized or not authorized at all — never a token the poll cannot reveal. - Existing ordering guarantee still holds under the raced-authorize test. - Tests use `PostgreSqlTestServer` and `RedisTestServer` in `tests/Jellyfin.Server.Tests` and run in the `postgres-migration-chain` Woodpecker step. In-memory providers have hidden different semantics and are how three bugs reached production.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unkin/jellyfin-ha-src#35