[Bug][Prod] GameServer rejects ALL player JWTs — audience config 'spikersoft-api' vs Keycloak's actual 'account' (regression from #407 hardening) #491

Closed
opened 2026-07-12 01:01:31 +00:00 by spikerj · 1 comment
Owner

Reported live by Joey (Seq, 2026-07-11 ~20:5x): every game WebSocket connection logs Rejected invalid GameServer JWT (treating connection as unauthenticated) followed by ListShips/CreateShip/JoinZone failed: No user ID for connection ... - authentication required — for players with valid Keycloak sessions. Complete game-auth outage.

Root cause: the #407 hardening (backend PR #158) correctly replaced trust-the-sub-claim with real JWKS validation, but shipped Authentication:Audience = "spikersoft-api". This realm's Keycloak access tokens carry aud: "account" (Keycloak default — the same value the API validates via Keycloak:audience). Audience validation rejected every legitimate token. The validator's unit tests covered the mechanics with a synthetic audience; the config value was never tested against reality.

Fix: backend PR #214 — GameServer Authentication:Audience"account" + regression test GameServerAudience_MatchesApiKeycloakAudience asserting the GameServer and API configs agree on the audience (they validate the same tokens).

Verify: after merge/deploy, the Rejected invalid GameServer JWT stream in Seq stops and in-game ListShips/CreateShip/JoinZone succeed for a logged-in player.

Follow-up option (separate scope): add a Keycloak audience mapper so tokens carry a real API audience, then tighten both services in lockstep.

Reported live by Joey (Seq, 2026-07-11 ~20:5x): every game WebSocket connection logs `Rejected invalid GameServer JWT (treating connection as unauthenticated)` followed by `ListShips/CreateShip/JoinZone failed: No user ID for connection ... - authentication required` — for players with valid Keycloak sessions. Complete game-auth outage. **Root cause:** the #407 hardening (backend PR #158) correctly replaced trust-the-sub-claim with real JWKS validation, but shipped `Authentication:Audience = "spikersoft-api"`. This realm's Keycloak access tokens carry `aud: "account"` (Keycloak default — the same value the API validates via `Keycloak:audience`). Audience validation rejected every legitimate token. The validator's unit tests covered the mechanics with a synthetic audience; the config *value* was never tested against reality. **Fix:** backend PR #214 — GameServer `Authentication:Audience` → `"account"` + regression test `GameServerAudience_MatchesApiKeycloakAudience` asserting the GameServer and API configs agree on the audience (they validate the same tokens). **Verify:** after merge/deploy, the `Rejected invalid GameServer JWT` stream in Seq stops and in-game ListShips/CreateShip/JoinZone succeed for a logged-in player. Follow-up option (separate scope): add a Keycloak audience mapper so tokens carry a real API audience, then tighten both services in lockstep.
Author
Owner

Resolved in spikersoft-backend PR #214 (merged to master; CI auto-deploys the gameserver). GameServer Authentication:Audience corrected from 'spikersoft-api' to 'account' — the value this realm's Keycloak access tokens actually carry and the same one the API validates. Regression test GameServerAudience_MatchesApiKeycloakAudience now pins the two services' configs together so they can never disagree on the audience again. Verify as the deploy lands: the 'Rejected invalid GameServer JWT' stream in Seq stops, and ListShips/CreateShip/JoinZone succeed in-game for a logged-in player. If anything still rejects after the rollout, reopen — the next suspect would be issuer mismatch, and the Seq event's exception detail (IDX code) will say which. Closing.

Resolved in spikersoft-backend PR #214 (merged to master; CI auto-deploys the gameserver). GameServer Authentication:Audience corrected from 'spikersoft-api' to 'account' — the value this realm's Keycloak access tokens actually carry and the same one the API validates. Regression test GameServerAudience_MatchesApiKeycloakAudience now pins the two services' configs together so they can never disagree on the audience again. Verify as the deploy lands: the 'Rejected invalid GameServer JWT' stream in Seq stops, and ListShips/CreateShip/JoinZone succeed in-game for a logged-in player. If anything still rejects after the rollout, reopen — the next suspect would be issuer mismatch, and the Seq event's exception detail (IDX code) will say which. Closing.
Sign in to join this conversation.