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>/callbackfor desktop and the editor, your custom scheme for mobile (for examplexyz.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
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.addressThe listener binds 127.0.0.1 only. Pick a port you registered.
#Mobile: deep link
On iOS and Android, register a custom URL scheme for the app and use DeepLinkRedirect instead of the loopback listener:
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
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:
- Fetch the issuer's JWKS from its OpenID configuration (
/.well-known/openid-configuration,jwks_uri). - Check signature,
iss,aud(your client id),expandnonce. - Use
subas the stable player id, andgully_accountfor 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.