External game client authentication
A framework v3 game can let players sign in with Boardweaver from an external client, such as a future Godot desktop app. The game's creator needs the Professional plan. Players need a Boardweaver account, but no paid plan. Platform admins also have this entitlement.
Framework v3 is an opt-in preview available to everyone. This feature supplies authentication and permission enforcement, not a Godot addon, native exports, matchmaking, or the bundled frontend's commit compatibility checks.
Register a client
A game administrator opens Game clients under Access in the studio. Register one public OAuth client per game with its callback URL. A public client has no secret: never put a developer session token in a shipped game.
Desktop callbacks use a loopback IP, such as
http://127.0.0.1:3000/callback or http://[::1]:3000/callback. The
authorization request may use a different port; its host, path and query must
match the registered callback. HTTPS callbacks are also supported. URLs with
credentials or fragments and HTTP callbacks on other hosts are refused.
Registration follows the plan of the game's creator (its earliest admin member), not that of the collaborator registering it. Only v3 games can register. Disabling a client revokes all its grants and pending authorization codes. Re-enabling it requires the creator's Professional entitlement and does not restore revoked grants.
The management API requires a first-party session token:
| Request | Body or query | Result |
|---|---|---|
POST /api/game-clients |
{ gameDefinitionId, redirectUris: [...] } |
OAuth registration, including client_id, callback URLs and scope. |
GET /api/game-clients?gameDefinitionId=<id> |
Game administrator only. | { eligibility: { creatorEntitled, frameworkV3 }, client }, where client is { clientId, disabled, redirectUris } or null. |
PUT /api/game-clients/<clientId> |
{ disabled: true } or { disabled: false }. |
{ success: true }. |
The grant scope is play:<gameDefinitionId>. Dynamic MCP client registration
at /register cannot create a game client or obtain a play grant.
Browser sign-in with PKCE
- Generate a cryptographically random PKCE verifier and an independent
state. Derive the S256 challenge: SHA-256 of the verifier, encoded as base64url without padding. - Open the system browser at Boardweaver's
/authorizeendpoint withresponse_type=code,client_id,redirect_uri,state,code_challenge,code_challenge_method=S256and the registeredscope. Omitresourcefor play grants. - Boardweaver asks the player to sign in and approve access. The consent screen uses the registered game's name and designer, and describes game-only access.
- The callback receives
codeandstate, orerror=access_denied. Checkstateagainst the original attempt before accepting it. Bind a desktop callback listener only to loopback, close it after use, and implement cancellation and a timeout. - Exchange the code at
POST /token, usingapplication/x-www-form-urlencodedwithgrant_type=authorization_code,client_id,code,code_verifierand the exactredirect_uriused in step 2. The ephemeral port must also match. There is noclient_secret.
Authorization codes expire after ten minutes and can be redeemed once.
Tokens use the standard OAuth response fields: access_token,
refresh_token, token_type=bearer, expires_in=3600 and scope.
Keep refresh tokens in the operating system's secure credential store. The game should never collect a Boardweaver password.
Refresh, logout and revocation
Access tokens last one hour. Refresh tokens expire after 60 days and rotate
on every use. Send a form-encoded POST /token with
grant_type=refresh_token, client_id and refresh_token. Persist the
replacement refresh token securely before discarding the old one.
Serialize refresh attempts. A used refresh token is invalid, including a concurrent replay. If a refresh response is lost and the old token has been consumed, start browser sign-in again. Refresh cannot expand the grant's scope. Previously issued access tokens remain valid until their expiry unless the grant is revoked.
Each successful sign-in creates a separate grant, so signing out one install
does not sign out another. LogOut over the authenticated socket revokes
that grant and all its access and refresh tokens. OAuth clients may also
POST to /revoke with client_id and an access or refresh token.
Players manage their grants at Account settings > Connected games. Their first-party session can also call:
| Request | Result |
|---|---|
GET /api/game-clients/connections |
{ connections: [{ id, clientId, gameDefinitionId, name, createdAt }] }. |
DELETE /api/game-clients/connections/<grantId> |
Revokes only the caller's grant. |
The creator's entitlement, client availability and grant are checked when issuing or refreshing tokens, on authenticated socket requests, and before subscription updates. A creator downgrade blocks external-client access until the entitlement is restored. Deleted or disabled clients cannot obtain MCP credentials instead.
Connect to the socket
Open Boardweaver's /ws WebSocket using TLS in production. Send
SetClientId first, then attach the current access token to every request:
{
"requestId": "a-client-generated-request-id",
"token": "<play-access-token>",
"data": {
"type": "SetClientId",
"params": { "clientId": "an-install-or-connection-id" }
}
}
A play-authenticated SetClientId response contains data.playUser, with
the player's public id, username, avatar and creation date. It contains no
private params.user, email, plan, studio permissions or account-wide
match list, and cannot redeem a browser's readable sign-in token.
A socket keeps its first authenticated scope and game. It cannot switch
between a session and play token or between games; those attempts fail with
SCOPE_MISMATCH. A refreshed access token for the same game is accepted.
Missing, expired or revoked play credentials authorize no play requests.
The socket sends match updates only while the last access token it received
is valid, even when the client sends nothing. After each refresh, send
SetClientId again with the new access token before the old one expires.
When that token expires, the socket stops receiving updates without an error.
Send the new token and Subscribe again to resume them.
The currently allowed requests are:
SetClientId,LogOut,Unsubscribe.CreateMatch, naming this game and a version channel, or a rematch source in this game.Subscribe, for this game's matches only.JoinMatch,StartMatch,CommitDecision,LeaveMatch,SetMatchFocus,ReportMatchActivity,SetSpectatorPolicy,SetMatchAudioSettings,GetMatchReplay,StopWatching.GetMatchSteps,GetMatchDraft,SaveMatchDraft,SetMatchSeen,Undo.FetchMatchFeedbackForm,SubmitMatchFeedback,DismissMatchFeedback.
Use GetMatchSteps to read the steps of a live or finished v3 match.
GetMatchReplay returns the finished match's replay metadata.
Every match target must belong to the grant's game. Private beta matches
require game membership. Event tables are excluded. Successful CreateMatch,
JoinMatch and Subscribe requests bind existing seats to the external client and lock the lobby's game selection. A
subscription does not create seats for spectators. A rejected request or
subscription batch leaves seat bindings unchanged. Other allowed requests
authorize game access without binding seats or locking game selection.
Studio, admin, account-wide and MCP requests, and subscriptions to other
models such as me, user or worktree, are refused. Play tokens do not
authenticate first-party HTTP routes or /mcp. Match permissions such as
host-only starting and legal moves still apply. The v3 gameplay protocol supports multi-step decisions and replay.