Skip to content

03 — Logging in

The sample in chapter 02 connects with an arbitrary token to a local server — fine for a listen-server on 127.0.0.1. For a real game you want a player identity: an access token minted by the lattice-auth service. That token authenticates the player to the social service and (via the director) to the game server.

This chapter shows two ways to get a token:

  1. the LatticeSocialClient SDK (a .NET helper that wraps the calls), and
  2. the raw REST endpoints (call them from Unity with UnityWebRequest or HttpClient).

Verified against: control-plane/lattice-social-client/src/LatticeSocialClient.SignIn.cs, control-plane/lattice-auth/src/LatticeAuth/Program.cs, control-plane/lattice-auth/src/LatticeAuth/Api/Contracts.cs.

The SDK is a separate .NET library, not part of com.lattice.netcode

LatticeSocialClient lives in control-plane/lattice-social-client/ and is a standalone .NET library. To use it in Unity you'd reference it as a managed DLL. If you'd rather not add a dependency, the endpoints are plain JSON HTTP — call them directly with UnityWebRequest. Both paths hit the same lattice-auth service and produce the same token.

The auth endpoints

lattice-auth exposes these (bodies are snake_case JSON). In local dev it runs on port 3005.

Endpoint Body Returns Auth
POST /guest { region?, device_fingerprint? } token response none
POST /register { email, password, region? } { account_id }no token none
POST /login { email, password, device? } token response none
POST /identity { provider, ticket, device? } token response none
POST /platform { provider, ticket, device? } token response none
POST /refresh { refresh_token } token response none
GET /account account info Bearer

Verified against: lattice-auth/src/LatticeAuth/Program.cs:127-190, lattice-auth/src/LatticeAuth/Api/Contracts.cs:4-21; port from run-all.sh:24.

The token response is:

POST /login → 200
{
  "access_token": "eyJ…",
  "refresh_token": "…",
  "expires_in": 3600,
  "account_id": "5f3c…"
}

Verified against: Contracts.cs:17-21 (TokenResponse(access_token, refresh_token, expires_in, account_id)), serialized with PropertyNamingPolicy = null at Program.cs:98.

/register does not return a token

Registration creates the account and returns only { account_id }. Call POST /login afterward to get an access token.
Verified against: Program.cs:133-144.

Option A — the LatticeSocialClient SDK

Point the SDK at your auth deployment with UseAuth, then call one of the SignIn* methods. On success the SDK applies the token as its own bearer identity automatically, so the very next social call is authenticated.

Sign in with the SDK
using LatticeSocial.Client;

var social = new LatticeSocialClient(new Uri("http://localhost:3009")); // social service
social.UseAuth(new Uri("http://localhost:3005"));                       // auth service

// Guest (anonymous) — no credentials needed:
LatticeSession session = await social.SignInAsGuestAsync(region: "eu");

// …or email + password:
LatticeSession session = await social.SignInWithEmailAsync("player@example.com", "hunter2");

// …or a third-party ticket (Steam/Epic/Google/Microsoft/Apple):
LatticeSession session = await social.SignInWithThirdPartyAsync(IdentityProvider.Steam, ticket);

string accessToken = session.AccessToken;   // pass this on to the director (chapter 04)

Verified against: LatticeSocialClient.SignIn.cs:27-83 (UseAuth, SignInAsGuestAsync, SignInWithEmailAsync, SignInWithThirdPartyAsync), and ApplySession at LatticeSocialClient.SignIn.cs:99-103 which calls UseBearerToken(session.AccessToken).

The session object:

LatticeSession
public sealed record LatticeSession(
    string AccessToken,        // JSON: access_token
    string RefreshToken,       // JSON: refresh_token
    int    ExpiresInSeconds,   // JSON: expires_in
    string AccountId);         // JSON: account_id

Verified against: control-plane/lattice-social-client/src/Identity.cs:43-47.

The provider enum

SignInWithThirdPartyAsync accepts an IdentityProvider; its wire value is the lowercase name.

public enum IdentityProvider { Steam, Epic, Google, Microsoft, Apple }

Verified against: Identity.cs:15-36.

Option B — raw REST from Unity

If you don't want the SDK dependency, call the endpoint directly. Any HTTP client works; here's UnityWebRequest:

Guest login with UnityWebRequest
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

[System.Serializable] class GuestReq  { public string region = "eu"; }
[System.Serializable] class TokenResp { public string access_token; public string refresh_token;
                                        public int expires_in; public string account_id; }

IEnumerator GuestLogin(System.Action<string> onToken)
{
    var body = JsonUtility.ToJson(new GuestReq());
    using var req = new UnityWebRequest("http://localhost:3005/guest", "POST");
    req.uploadHandler   = new UploadHandlerRaw(Encoding.UTF8.GetBytes(body));
    req.downloadHandler = new DownloadHandlerBuffer();
    req.SetRequestHeader("Content-Type", "application/json");
    yield return req.SendWebRequest();

    if (req.result != UnityWebRequest.Result.Success) { Debug.LogError(req.error); yield break; }
    var resp = JsonUtility.FromJson<TokenResp>(req.downloadHandler.text);
    onToken(resp.access_token);
}

The email-login variant just posts to /login with { "email": …, "password": … } and reads the same TokenResp.

Verified against: the request/response shapes in Contracts.cs:4-21 and the handlers in Program.cs:127-156.

What the token is for

Keep the access_token. You'll present it as a Bearer token to:

  • the director (/matchmake, /resolve) to find and join a game server. The binding's Matchmaker helper takes the access token and does that exchange for you — see chapter 04; and
  • the social service (friends/presence/parties), which the SDK does for you after sign-in.

Verified against: lattice-director/src/LatticeDirector/Program.cs:141-160 (matchmake requires a valid bearer via PlayerAuthenticator.Authenticate).

Gotchas and honesty notes

Third-party sign-in is fail-closed by default

Steam/Epic/Google/etc. ticket verification is rejected unless the deployment opts into the dev/test stub verifier with the environment variable LATTICE_ALLOW_STUB_VERIFIER=1. The stub does no real cryptography — it trusts a "<provider>:<subject>" ticket — so it is strictly a development convenience. Email login and guest login work without it.
Verified against: lattice-auth/src/LatticeAuth/Program.cs:39-51.

Rate limiting

/login, /register, /platform, /identity are limited to ~20 requests/min/IP; /guest to ~10/min/IP. A tripped window returns HTTP 429. The in-process test host disables this with LATTICE_DISABLE_RATE_LIMIT=1.
Verified against: Program.cs:62-88.


Next: 04 — Lobbies & matchmaking, to turn that token into a game server endpoint.