Gully ID sign-in

On this page

Gully ID is the one identity across every Gully product: passkey sign-in and a smart account per player, no seed phrase inside a game. Games sign players in with standard OpenID Connect, authorization code flow with PKCE, as a public client. The SDK's GullyIdClient does the protocol work.

Coming soon

Gully ID (https://id.gullychain.xyz) is built but not deployed yet; the issuer does not answer today. The code on this page is final, and sign-in works once Gully ID is live. The developer portal (opens in a new tab) shows its status.

#Get a client id

Each game needs its own public OIDC client, registered by Gully Labs on Gully ID. Ask for one at [email protected] with:

  • the game name,
  • the redirect URIs: http://127.0.0.1:<port>/callback for desktop and the editor, your custom scheme for mobile (for example xyz.gullystudio.<game>:/callback).

Public clients have no secret (token_endpoint_auth_method: none) and PKCE is enforced.

#Default configuration

Field Default
Issuer https://id.gullychain.xyz (GullyChain.GullyIdIssuer)
ClientId yours, from registration
RedirectUri loopback or custom scheme
Scopes openid, email, gully.account, offline_access

Endpoints the client uses, all relative to the issuer: /oauth2/auth, /oauth2/token, /oauth2/sessions/logout, /v1/me.

#Desktop and editor: loopback redirect

C#
using GullyLabs.GameSdk;
using UnityEngine;

using var listener = new LoopbackRedirectListener(53682);
var id = new GullyIdClient(new GullyIdConfig { ClientId = "my-game", RedirectUri = listener.RedirectUri });

var auth = id.BeginAuthorization();          // builds the URL, state and PKCE verifier
Application.OpenURL(auth.Url);               // the system browser shows the passkey prompt
var code = GullyIdClient.ParseCallback(await listener.WaitForCallbackAsync(), auth.State);
var session = await id.ExchangeCodeAsync(code, auth);

// session.Claims.sub, session.Claims.email, session.Claims.gully_account.address

The listener binds 127.0.0.1 only. Pick a port you registered.

On iOS and Android, register a custom URL scheme for the app and use DeepLinkRedirect instead of the loopback listener:

C#
var id = new GullyIdClient(new GullyIdConfig { ClientId = "my-game", RedirectUri = "xyz.gullystudio.mygame:/callback" });
var auth = id.BeginAuthorization();
Application.OpenURL(auth.Url);
var redirected = await DeepLinkRedirect.WaitForCallbackAsync("xyz.gullystudio.mygame:/callback");
var session = await id.ExchangeCodeAsync(GullyIdClient.ParseCallback(redirected, auth.State), auth);

#Refresh and sign out

C#
var fresh = await id.RefreshAsync(session.RefreshToken);   // needs the offline_access scope
string me = await id.GetMeRawAsync(fresh.AccessToken);      // raw JSON from /v1/me
Application.OpenURL(id.LogoutUrl());

Store refresh tokens with the platform's secure storage (Keychain, Android Keystore), never in PlayerPrefs.

#Verify tokens on your server

IdToken decodes claims in the client for display only; it does not check the signature. Anything that grants value (a game server session, a purchase, a reward) must verify the id_token or access token on your server:

  1. Fetch the issuer's JWKS from its OpenID configuration (/.well-known/openid-configuration, jwks_uri).
  2. Check signature, iss, aud (your client id), exp and nonce.
  3. Use sub as the stable player id, and gully_account for the player's on-chain address.

For multiplayer, Nakama does this step: the game hands the token to Nakama's custom authentication. See Multiplayer.

#Claims

Claim What
sub stable Gully ID user id
email the player's email, with the email scope
gully_account the player's smart account on Gully Chain, with the gully.account scope

#Next

  • Wallet: send transactions from the signed-in player's account.