-
Notifications
You must be signed in to change notification settings - Fork 2
Custom Token Sign In
Custom tokens let a developer-run backend own player identity. The backend signs a short-lived JWT for a player, the game client exchanges it at POST /v1/auth/custom-token, and ggscale verifies the signature and mints a session. Use it when you already have accounts (a website, an existing game backend) and want ggscale to follow them.
Verification is public-key based. Your backend signs with a private key that never leaves your infrastructure, and ggscale stores only the public key. A ggscale database leak therefore exposes nothing that can mint player sessions.
Breaking change. Before 2026-08, custom tokens used a shared HS256 secret. That scheme is removed. Generate a keypair, switch your signer to EdDSA (or RS256), and save the public key in the control panel.
Ed25519 is recommended; it is small, fast, and misuse-resistant:
openssl genpkey -algorithm ed25519 -out custom-token-private.pem
openssl pkey -in custom-token-private.pem -pubout -out custom-token-public.pemRSA (2048-bit minimum) also works if your JWT library lacks EdDSA support; sign with RS256.
In the control panel, open Account Tenant settings → Custom token sign-in and paste the contents of custom-token-public.pem. Saving an empty value disables custom-token sign-in. The private key stays on your backend.
The token must carry external_id (your stable user id), a short exp, and the audience ggscale-custom-token:
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.MapClaims{
"external_id": "user-42",
"aud": "ggscale-custom-token",
"exp": time.Now().Add(5 * time.Minute).Unix(),
})
signed, err := token.SignedString(ed25519PrivateKey)Keep exp short, in minutes. The token is single-purpose: your backend hands it to the game client, and the client exchanges it immediately.
curl -s -X POST http://localhost:8080/v1/auth/custom-token \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{"token":"eyJhbGciOiJFZERTQSIs..."}'The response is the standard session shape (access_token, refresh_token, player_id, expires_at). The first exchange for an external_id creates the player, and later exchanges return the same player.
| Status | Meaning | Fix |
|---|---|---|
| 400 | No public key configured for the Account Tenant. | Save the key in the control panel. |
| 401 | Signature, algorithm, aud, or exp check failed. |
One opaque error by design; check key, algorithm (EdDSA/RS256), audience, and clock. |
| 403 | Player banned, or the Account Tenant hit its player limit. | Not a token problem. |
Save a new public key in the control panel and switch your backend's private key at the same time. Tokens signed with the old key stop verifying immediately; they live for minutes, so a brief deploy-window overlap is the only coordination needed.
Full reference: the /v1/auth/custom-token operation.