Skip to main content

Native Gaming Identity

Native gaming identity lets a player sign in with the account they already have on their platform — Apple Game Center on iOS or Google Play Games (GPGS) on Android — with no email, password, or OAuth redirect. The game produces a platform-signed identity on-device; Zyphr verifies it server-side and issues a normal Zyphr end-user session (access token + refresh token).

This is the right fit for:

  • Mobile games where players expect to be recognized by their Game Center / Play Games account
  • Cross-device continuity — the same platform player id resolves to the same Zyphr user
  • Frictionless sign-in — no account-creation step, no password to manage

Zyphr supports two providers today: game_center (Apple) and google_play_games (Google). Both terminate the same way — verify → provision-or-look-up the end user by (provider, playerId) → mint a session.

One application per game

Each game is its own Zyphr application. Players are isolated per game: there is no shared cross-game identity, and gaming sign-in is not organization-scoped. A game's Game Center bundle IDs and Google Play Games credential both live on that one application's provider config. If you ship multiple games, create one Zyphr application per game.

Integration shape (backend-relayed)

The identity artifact can only be produced on-device by the game — there is no server-side way to mint it. The recommended shape is backend-relayed: the game client produces the artifact, hands it to your backend, and your backend relays it to Zyphr with your application key. The game client never holds a Zyphr key.

Game client (device)                Your backend                 Zyphr
produce signed identity ──────▶ relay to Zyphr ──────▶ verify server-side
(GameKit / GPGS) (X-Application-Key) provision-or-lookup
mint session ◀── tokens

Authentication

Both gaming endpoints are public-key only — no secret key required, because the trust comes from the platform-signed identity, not from a Zyphr secret. In the backend-relayed shape you still send the key from your server.

HeaderValueRequired
X-Application-KeyYour app's public key (za_pub_xxxx)Always
Content-Typeapplication/jsonYes

Both endpoints return 201 when a new player is provisioned and 200 for a returning player; both return the standard { access_token, refresh_token, ... } auth result. On any verification failure the response is a single opaque 401 — the specific control that failed is never disclosed.

Apple Game Center

Producing the assertion (on-device, iOS)

The iOS app calls GameKit's GKLocalPlayer.fetchItems(forIdentityVerificationSignature:), which returns publicKeyURL, signature, salt, and timestamp. Combine those with the player id and the app's bundle id and relay all six fields to Zyphr.

Use teamPlayerID, not gamePlayerID or the legacy playerID

Apple computes the identity-verification signature over GKLocalPlayer.teamPlayerID (form T:_…). Send that value as playerId. Sending gamePlayerID (form A:_…) — a different modern id — makes the signature fail to verify even though everything else is correct. Do not send the deprecated playerID either. Your player's stable Zyphr identity is keyed on teamPlayerID.

Endpoint

POST /v1/auth/game-center

Request body — all six fields are required:

FieldTypeNotes
playerIdstringThe scoped teamPlayerID from GameKit (the value Apple signed over).
publicKeyUrlstringApple-hosted public-key URL from GameKit (must be an *.apple.com host).
signaturestringBase64-encoded RSA signature from GameKit.
saltstringBase64-encoded salt bytes from GameKit.
timestampnumberApple-supplied timestamp (epoch milliseconds) from GameKit.
bundleIdstringThe app's iOS bundle identifier — must be in the trusted allow-list you configured.
curl -X POST https://api.zyphr.dev/v1/auth/game-center \
-H "X-Application-Key: za_pub_xxxx" \
-H "Content-Type: application/json" \
-d '{
"playerId": "T:_abc123…",
"publicKeyUrl": "https://static.gc.apple.com/public-key/gc-prod-9.cer",
"signature": "base64-rsa-signature",
"salt": "base64-salt",
"timestamp": 1785000000000,
"bundleId": "com.yourcompany.yourgame"
}'

Zyphr independently re-verifies the assertion before minting: it fetches the Apple public key (validating the certificate chain), checks the RSA-SHA256 signature over the exact signed payload, enforces a freshness window with a single-use replay guard, and confirms the bundleId is in your trusted allow-list.

Dashboard configuration

Configure Game Center under Applications → [Your App] → OAuth → Native gaming identity. Enable Game Center and add the trusted bundle IDs Zyphr will accept (e.g. com.yourcompany.yourgame). An assertion whose bundleId is not in the allow-list is rejected. Game Center needs no client id or secret — its trust boundary is the bundle-id allow-list plus Apple's signature.

Google Play Games (GPGS)

GPGS is a different protocol from Game Center — there is no client-side signature. Instead the device obtains a single-use server auth code, and Google verifies it: Zyphr exchanges the code server-to-server, then reads the trusted player id from Google's Play Games API.

Producing the server auth code (on-device, Android)

The Android app calls GamesSignInClient.requestServerSideAccess(serverClientId, …) using the Google Cloud web (server) client id configured for the game. That returns a single-use auth code string. Relay it to Zyphr as serverAuthCode.

The player id is server-derived — never client-supplied

Zyphr obtains the player id only from its own server-side call to Google after exchanging the auth code. A player id sent in the request body is neither read nor trusted. This is what makes GPGS sign-in tamper-proof.

Endpoint

POST /v1/auth/google-play-games

Request body:

FieldTypeNotes
serverAuthCodestringThe single-use server auth code from GamesSignInClient.requestServerSideAccess, obtained with the game's web/server client id.
curl -X POST https://api.zyphr.dev/v1/auth/google-play-games \
-H "X-Application-Key: za_pub_xxxx" \
-H "Content-Type: application/json" \
-d '{ "serverAuthCode": "4/0AeaYSH…" }'

Zyphr exchanges the code at Google's OAuth2 token endpoint using your per-game client id and secret, then calls the Play Games API (players/me) to obtain the trusted playerId, and provisions-or-looks-up the end user on it.

Dashboard configuration

Configure Google Play Games under Applications → [Your App] → OAuth → Native gaming identity. Enable Google Play Games and provide:

  • OAuth2 web client id — the web/server client id from your Google Cloud project (the same one the Android app passes to requestServerSideAccess).
  • OAuth2 client secret — the secret paired with that client id. It is stored encrypted and write-only: the dashboard shows only whether a secret is configured, never its value. Leave the field blank on a later save to keep the existing secret, or enter a new value to replace it.

Sign in with the SDK

The Zyphr SDKs take the already-produced artifact and call the endpoint for you — they do not call GameKit / Play Games Services themselves (that stays in your native game code). Both signInWithGameCenter and signInWithGooglePlayGames are available in the Node, Swift, Kotlin, and C# SDKs.

import { ZyphrClient } from '@zyphr-dev/node-sdk';

const zyphr = new ZyphrClient({ applicationKey: process.env.ZYPHR_APP_PUBLIC_KEY });

// Apple Game Center (fields captured on-device via GameKit)
const gc = await zyphr.auth.signInWithGameCenter({
signInWithGameCenterRequest: {
playerId, // teamPlayerID
publicKeyUrl,
signature,
salt,
timestamp,
bundleId,
},
});

// Google Play Games (server auth code captured on-device)
const gp = await zyphr.auth.signInWithGooglePlayGames({
signInWithGooglePlayGamesRequest: { serverAuthCode },
});

Both return the standard auth result with an access token and refresh token you can use like any other Zyphr session.

Connections in the dashboard

A player who has signed in with a gaming provider appears as a first-class connection on the end user's detail view (Applications → [Your App] → Users → [user]), showing the provider and the platform player id it is keyed on. Unlike OAuth connections, gaming identities are the player's sole login for the game and are managed through the provider config rather than unlinked per user.

  • OAuth & Social Login — the redirect-based social providers.
  • Anonymous Sign-In — issue an identity with no credential, convert later.
  • API reference: POST /v1/auth/game-center and POST /v1/auth/google-play-games.