Skip to content

Verifying Players

If your game has its own backend — saving progress server-side, running a shop, awarding anything of value — it must not take the player's identity from the game client. Verify it against Y8 instead.

The player object is display data, not proof of identity

Everything the SDK gives you about the signed-in player, the pid included, lives in the browser and can be modified by whoever is playing. It is there so you can greet someone by name and show their avatar.

A player who edits their pid before your game sends it to your server is then, as far as your server knows, somebody else.

If your game has no backend of its own, there is nothing to do here — the SDK already authenticates every call it makes to Y8 on your behalf.


How it works

Three steps, and the important part is that step 3 happens on your server, not in the game:

  1. The player signs in and the SDK reports it.
  2. The game asks the SDK for the player's access token and sends that token — and nothing else — to your backend.
  3. Your backend asks Y8 who the token belongs to, and believes Y8's answer.

The token is the only thing that crosses. Your backend never receives a player id from the client, so there is nothing for a player to tamper with.


Step 1 and 2 — in the game

const accessToken = y8Sdk.getAccessToken();

if (accessToken) {
    await fetch("https://your-game-backend.example/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ accessToken }),
    });
}

getAccessToken() is synchronous and returns null when nobody is signed in, so call it after authentication has been reported.

Send the access token, never the full token object

There is also a getToken() method, which returns the whole token object including the refresh token. A refresh token can mint new access tokens, so it must never leave the SDK — not to your own backend, not anywhere. getAccessToken() returns only the single string your server needs.

JsResponse<Y8Token> response = await Y8.Instance.GetTokenAsync();

if (response.IsSuccess)
{
    string accessToken = response.Data.access_token;

    // Send accessToken — and only that — to your backend.
}

IsSuccess is false when nobody is signed in, so call it after authentication has been reported.

Send the access token, never the full token object

Y8Token is the whole token object including the refresh token. A refresh token can mint new access tokens, so it must never leave the game — not to your own backend, not anywhere. Send only access_token.

Functions.getToken returns the whole token as a JSON string ("null" when nobody is signed in). Load it into a JSON object, take JSON.Get("access_token"), and send that value — and only that — to your backend, for example with the AJAX object.

Send the access token, never the full token object

The JSON string from getToken includes the refresh token. A refresh token can mint new access tokens, so it must never leave the game — not to your own backend, not anywhere. Send only the access_token value, never the string itself.

var token = Y8.getToken();

if (token != null)
{
    var accessToken:String = token.access_token;

    // Send accessToken — and only that — to your backend.
}

Y8.getToken() returns null when nobody is signed in, so call it after authentication has been reported.

Send the access token, never the full token object

Y8.getToken() returns the whole token object including the refresh token. A refresh token can mint new access tokens, so it must never leave the game — not to your own backend, not anywhere. Send only access_token.


Step 3 — on your server

Ask Y8 who the token was issued to:

GET https://account.y8.com/api/profile
Authorization: Bearer <access_token>

A 200 response describes the player the token actually belongs to:

{
  "nickname": "…",
  "level": 1,
  "trust_details": { "email": "…" },
  "pid": "…",
  "locale": "en",
  "avatars": {
    "thumb_url": "…",
    "thumb_secure_url": "…",
    "medium_url": "…",
    "medium_secure_url": "…",
    "large_url": "…",
    "large_secure_url": "…"
  },
  "version": "…",
  "client_id": "…"
}
Field Type Description
pid string The player's id in your game; the same player has a different pid in every other game. The one to trust.
client_id string The App ID the token was issued to. Compare it with yours.
nickname string Display name.
level number 1 if the player has confirmed their email, otherwise 0.
trust_details.email string or null When the player confirmed their email, or null if they have not.
locale string The player's language setting, e.g. "en".
avatars object Avatar URLs at 32, 50 and 160 px (thumb, medium, large). Each is null when the player has no approved picture; *_secure_url repeats *_url.
version string A hash of the profile that changes whenever the player edits it.

An app that Y8 has granted access to further profile fields, such as email, also receives those.

Then, on your server:

  1. Use the pid from this response. Ignore any identity the client sent alongside the token.
  2. Check that client_id equals your own App ID. Reject the login if it does not — see below.
  3. On 401, reject the login. The token is invalid or expired. Have the game call refreshToken() and retry once with the new token.
  4. Verify on every sign-in, not once per account, then issue your own session keyed to the verified pid.

Why the client_id check matters

Access tokens are issued per application. Without step 2, a token obtained from a different game could be replayed against your backend, and the profile lookup would succeed — it is a perfectly valid token, just not one issued to you. Comparing client_id to your own App ID is what makes the check specific to your game.


What to store

Store the verified pid and key your own records to it. It stays the same for a player in your game across sessions and devices, but it is specific to your game: the same player has a different pid in every other game, so it cannot be used to match players across games.

Do not store the access token as a long-lived credential. It expires, and once your own session exists you no longer need it — you have already established who the player is.