Steam integration
Braket is Steam-native. This chapter explains the three tiers of Steam access, exactly what each unlocks, how a publisher Web API key differs from a normal one, and how keys and tickets are stored and handled. The distinction matters because some Braket features can only work with a publisher (partner) key, and you should not expect a normal key to do more than it can.
Tier 1: OpenID sign-in (no keys)
Player authentication uses Steam OpenID. This requires no API key from you or from the player. When a player signs in at /auth/login, Steam redirects back to your arena with a verified SteamID64, and Braket creates or updates the player's users row (steamId64, personaName, avatarUrl, isAdmin). The OpenID realm is your arena's public origin (tenants.baseUrl, which follows your custom domain when set).
With only OpenID you get: sign-in, registration, ready-up, player-reported results, disputes, standings, brackets, ladder, and the full REST API. You do not get rich persona/avatar refresh from Steam, trophies, or game-verified results, because those need Web API access.
In local development and sandbox arenas, Braket runs in mock Steam mode when no keys are present (config.steam.mock is true when STEAM_MOCK=1, or automatically true whenever STEAM_WEB_API_KEY is unset). Mock mode lets you sign in as fixture personas and exercise the entire lifecycle with no real Steam calls.
Tier 2: a normal Steam Web API key
A normal Steam Web API key (any developer can obtain one) unlocks the read-only player-data endpoints:
GetPlayerSummaries: canonical persona names and avatars, refreshed from Steam rather than only what OpenID hands back.- Friends: friend lists, used for Steam friend invites into tournaments.
Configuration:
- Environment (single-tenant/legacy):
STEAM_WEB_API_KEY. - Per-arena: the vault secret of kind
web_api_key, set in Admin under Steam settings.
A normal key cannot grant trophies, read partner leaderboards, or verify auth tickets. Those are partner-only operations. If you try to build game-verified results on a normal key, ticket verification will not be available.
Tier 3: a publisher (partner) Web API key
A publisher Web API key is a partner-tier key tied to your app on Steamworks. It unlocks the operations that only a partner may perform:
- Steam trophies (Inventory Service): granting the top-three tournament trophy as a Steam Inventory Service item. Trophy grants are recorded in
trophy_grantswithsteamMechanism(mockorinventory_item) and asteamRefonce real. - Partner leaderboards: the leaderboards API is a partner feature; a normal key cannot write leaderboards.
AuthenticateUserTicket: verifying the single-use Steam Web API tickets that the game client sends when it submits a verified result. This is the linchpin of the game-client protocol (The game-client result protocol (dual attestation)): without a publisher key, the server cannot verify who is submitting a result.
Configuration:
- Environment (single-tenant/legacy):
STEAM_PUBLISHER_API_KEY. - Per-arena: the vault secret of kind
publisher_api_key, set in Admin under Steam settings. - Your AppID: environment
STEAM_APP_ID(default4273350) or per-arenatenants.steamAppId.
The publisher key is required in real mode for game-client ticket verification; mock mode fakes verification so you can develop locally without it.
How publisher-only features differ from a normal key
The important design rule is that publisher-only capabilities are cleanly separated from what a normal key can do, so you always know which key a feature needs:
| Capability | Normal Web API key | Publisher Web API key |
|---|---|---|
GetPlayerSummaries (persona, avatar) |
Yes | Yes |
| Friend lists (Steam friend invites) | Yes | Yes |
| Steam Inventory Service trophies | No | Yes |
| Partner leaderboards | No | Yes |
AuthenticateUserTicket (verify game tickets) |
No | Yes |
If a feature depends on trophies, leaderboards, or verifying tickets, it needs the publisher key. If it only reads public player data, the normal key is enough. Braket keeps the two keys as separate vault secrets (web_api_key and publisher_api_key) precisely so the boundary is explicit.
The AuthenticateUserTicket flow
Game-verified results authenticate with a Steam Web API ticket, not with cookies or sessions:
- The game client calls
ISteamUser::GetAuthTicketForWebApi("braket")to mint a fresh, single-use, app-bound ticket, and sends it asAuthorization: Steam <ticket-hex>on the discovery and result endpoints. - Braket verifies the ticket server-side by calling
ISteamUserAuth/AuthenticateUserTicketwith the publisher Web API key,identity=braket, and your AppID. - Braket accepts the ticket only when the result is
OK, theownersteamidequals thesteamid(so family-shared submissions are rejected), andvacbannedisfalse. The returnedsteamidis the authenticated submitter; the client never states its own identity.
Because tickets are single-use and app-bound (bound to the identity string and your AppID), a ticket minted for one game cannot authenticate against another arena, and a captured ticket cannot be replayed. In mock mode, the server accepts Steam MOCK:<steamId64> so local development works without Steam.
The wire details (headers, endpoints, error codes) are in The game-client result protocol (dual attestation); the Unreal implementation that mints the ticket is in Unreal Engine SDK reference.
Key storage and secrets
Steam keys are secrets and are treated as such:
- Never committed. Keys live in environment variables (legacy single-tenant) or in the per-arena encrypted vault (multi-tenant). They are never checked into source.
- Encrypted at rest. Vault secrets (
web_api_key,publisher_api_key, and alsosmtp_pass) are stored as AES-256-GCM ciphertext, with each data-encryption key wrapped by a hybrid X25519 + ML-KEM-768 construction to the platform vault keypairs. Plaintext never appears in the database, backups, logs, or data exports. See Trust and data. - Set once, used server-side. You enter a key in Admin; it is sealed into the vault and only ever decrypted transiently to make a Steam call. The Trust page and data export show only ciphertext metadata (algorithm, timestamps, a ciphertext prefix), proving the plaintext is not exposed.
- Rotatable and revocable. Re-saving a key rotates it (a new DEK, the old row referenced as
rotatedFrom). Deleting your tenant crypto-shreds its live secret ciphertext.
The interim tenants.steamWebApiKey / tenants.steamPublisherApiKey plaintext columns exist only for the legacy first-party tenant and are empty for every studio arena; the encrypted vault is the storage path for studios.