ADR 0016. BFF Cookie Sessions and Machine OAuth
Status
Accepted.
Supersedes ADR 0003 (signed JWT at operator login for an embedded widget). Gameplay independence is ADR 0015.
Context
A partner-readable session JWT on every game action would put PlayerPal
claims in the hot path, encourage putting PII or live balance in tokens, and
depend on third-party cookies for widgets. Browser localStorage refresh
tokens are a theft target.
Decision
Player web: Authorization Code + PKCE through a backend-for-frontend.
Session is an HttpOnly, Secure, SameSite cookie. The browser token, if
any, carries the IAM subject, not the private player UUID and not live
loyalty state. Mapping iss + sub → player is server-side.
Embedded operator/provider UX: first-party PlayerPal popup, opaque
one-time codes, exact postMessage origin checks. No assertion or access
token in URLs. No third-party cookies. No refresh tokens in the widget.
Machine clients: OAuth 2.0 client credentials with private_key_jwt.
Access-token lifetime 5-15 minutes. Cached JWKS for validation. Key overlap
during rotation.
Consequences
- Claim-set and token-lifetime open questions from ADR 0003 are closed for the profiles above (spec §13).
- Embedded widgets cannot silently keep a PlayerPal session after popup close.
- See docs/authentication.md and spec §14.
Amendment
Local POC also establishes the BFF cookie via Nest Session/User API password
and passkey flows and Nest-owned social IdP success
(ADR 0024), not only Auth Code + PKCE through
Login V2. Cookies must be first-party on the portal host
(player.localhost path-routed Nest).