Auth: exchange a provider token (Steam, Google Play, OIDC) for a SpacetimeDB identity #31

Closed
opened 2026-08-19 15:42:14 +00:00 by yuris · 0 comments
Owner

The SDK handles the token it is given and persists the one the server issues. It has nothing for the step before that: turning a platform identity into a SpacetimeDB token. Today every consumer shipping past a prototype writes this themselves.

The Godot SDK ships it as a first-class feature: SpacetimeAuth (provider-agnostic OIDC exchange for Steam, Google Play, Epic; bounded retry with exponential backoff; timeout; credential redaction in logs) plus a JwtHelper that decodes unverified JWT claims to cache tokens per provider (nodes/spacetime_auth/spacetime_auth.gd, util/jwt_helper.gd).

Why this belongs in the SDK rather than in every game

Anonymous identity is fine until the player reinstalls, plays on a second device, or clears browser storage — then #17's persistence guarantees nothing, because the identity was never tied to anything but local storage. Every real game needs a durable identity, and the exchange is fiddly in the same ways for everyone: retries, timeouts, not logging the credential, caching per provider so a relaunch does not re-exchange.

The Defold pieces already exist: http.request for the exchange, and the injectable storage seam stdb.sys_storage for the cache.

Scope

  • Token exchange against a configurable OIDC issuer, returning a token usable as connect{ token = ... }
  • Bounded retry with backoff, and a timeout, on the exchange only — the socket reconnect is a separate mechanism and must not be entangled with it
  • Cache the exchanged token per provider through the existing storage seam
  • Never log the credential or the token. Redaction is not a nicety; a token in a log is a stolen identity
  • Decode JWT claims for cache keying without verifying them, and document that loudly — claims read this way are for cache bookkeeping only and must never gate anything

Explicitly out of scope: bundling any platform SDK. Obtaining a Steam ticket or a Play token is the game's job with the platform's own extension; this issue starts from "here is a provider token".

Acceptance

Unit tests against a stubbed HTTP seam covering success, retry-then-success, exhausted retries, timeout, and a malformed response. A documented worked example for one real provider. And a test asserting no credential or token appears in any log line the SDK emits.

Priority

Not a 1.0 blocker — a game can do this itself, and the SDK's job is arguably done once it accepts a token. But it is the most valuable item here for someone actually shipping, and the largest single gap against the peer SDK.

The SDK handles the token it is given and persists the one the server issues. It has nothing for the step before that: turning a platform identity into a SpacetimeDB token. Today every consumer shipping past a prototype writes this themselves. The Godot SDK ships it as a first-class feature: `SpacetimeAuth` (provider-agnostic OIDC exchange for Steam, Google Play, Epic; bounded retry with exponential backoff; timeout; credential redaction in logs) plus a `JwtHelper` that decodes unverified JWT claims to cache tokens per provider (`nodes/spacetime_auth/spacetime_auth.gd`, `util/jwt_helper.gd`). ## Why this belongs in the SDK rather than in every game Anonymous identity is fine until the player reinstalls, plays on a second device, or clears browser storage — then #17's persistence guarantees nothing, because the identity was never tied to anything but local storage. Every real game needs a durable identity, and the exchange is fiddly in the same ways for everyone: retries, timeouts, not logging the credential, caching per provider so a relaunch does not re-exchange. The Defold pieces already exist: `http.request` for the exchange, and the injectable storage seam `stdb.sys_storage` for the cache. ## Scope - Token exchange against a configurable OIDC issuer, returning a token usable as `connect{ token = ... }` - Bounded retry with backoff, and a timeout, **on the exchange only** — the socket reconnect is a separate mechanism and must not be entangled with it - Cache the exchanged token per provider through the existing storage seam - **Never log the credential or the token.** Redaction is not a nicety; a token in a log is a stolen identity - Decode JWT claims for cache keying **without verifying** them, and document that loudly — claims read this way are for cache bookkeeping only and must never gate anything Explicitly out of scope: bundling any platform SDK. Obtaining a Steam ticket or a Play token is the game's job with the platform's own extension; this issue starts from "here is a provider token". ## Acceptance Unit tests against a stubbed HTTP seam covering success, retry-then-success, exhausted retries, timeout, and a malformed response. A documented worked example for one real provider. And a test asserting no credential or token appears in any log line the SDK emits. ## Priority Not a 1.0 blocker — a game can do this itself, and the SDK's job is arguably done once it accepts a token. But it is the most valuable item here for someone actually shipping, and the largest single gap against the peer SDK.
yuris closed this issue 2026-08-20 15:03:33 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/spacetimedb-defold#31
No description provided.