Public game authentication

Beta: trusted backend mediation

The game signs players in with Firebase anonymous or Google authentication. Every reference-backend request must verify the Firebase ID token (signature, issuer, audience, expiry and revocation policy). Derive player identity only from verified UID. Tenant/project and permitted channel/item IDs are server configuration. Never trust body/query player, tenant or project identifiers. Keep DARTSTREAM_CLIENT_ID and DARTSTREAM_CLIENT_SECRET on the server and use the 0.3.3 machine client after publication.

Expose only load/save snapshot, read inventory, EMP consume and event publication. An EMP grant is authorized by a server-verified game action or one-time entitlement, not merely by the player’s request. Use stable, action-bound idempotency keys for both grant and consume; reject reuse with conflicting payloads. Enforce per-player and per-IP request limits, a small JSON body cap, event allowlists and an origin allowlist. A distributed deployment needs shared counters and idempotency storage; an in-memory counter is only a local-demo safeguard. Return sanitized 400/401/403/409/413/429/503 errors and never upstream credentials or internal diagnostics.

The reference sample must remove DS_CLIENT_SECRET entirely from Flutter source/build inputs. Before hosting, scan built main.dart.js for secret_ and the actual removed secret without printing it; both must have zero matches. If a secret was distributed or committed, revoke/rotate it through the owning credential workflow. Issue #156 tracks the actual sample implementation and end-to-end tests; this document is a contract, not a delivered sample.

Proposed public-client grant (requires Brian’s review)

No production grant is enabled by this design. An identity broker would verify Firebase identity and an application allowlist, then issue a five-minute audience-bound token with fixed tenant, project, player UID, jti and a narrow read-mostly scope set. Allowed operations: owned profile/inventory/session reads and constrained cloud-save access. No billing, credential administration, project administration or inventory grants. The server retains economic mutations.

Renewal would require a fresh verified Firebase ID token; no long-lived DartStream refresh secret in browser storage. Revoke a player/project grant via a server-side authorization epoch checked on every request; disabling a project must invalidate access. Use TLS, bounded clock skew, exact audience/issuer validation, token redaction, action-bound idempotency and shared rate limits. Bearer tokens remain replayable if stolen until expiry/revocation; jti alone does not prevent replay. Consider proof-of-possession only if the additional key lifecycle is justified by demonstrated risk.

Threat model: bundle extraction exposes only public Firebase configuration; forged UIDs and project IDs are rejected; stolen tokens have narrow scope and short lifetime; grant farming stays behind server entitlement checks; XSS can steal in-browser bearer tokens, so avoid persistent browser token storage and enforce the application’s CSP; retries cannot double-grant or consume; cross-tenant requests cannot override token claims; generic upstream proxying is forbidden.

Compared with mediation, the grant can reduce a hop and server read bandwidth, but it expands the public authorization surface and requires per-operation ownership enforcement, shared revocation and abuse infrastructure. Keep mediation for beta. Implement a public grant only after design approval and negative isolation/replay/revocation tests.