Authentication

There are two layers: API keys identify your app to the wallet, and session tokens identify a signed-in player. The wallet is the identity provider; your backend is the relying party that trusts it.

API keys

Publishable (pk_) — used by the SDK in the browser to start sign-in flows. Non-secret; you can re-view it any time in the portal.
Secret (sk_) — backend only. Authenticates server-to-server calls (session verification, the ledger). Shown once; rotate in the portal if lost.

Each key is bound to an environmenttest (devnet) or live (mainnet). The two are fully isolated: a test session can never be verified with a live key, and test funds never touch mainnet.

Sign-in methods

The SDK handles all of these; they end in the same session. Enable the ones you want per app in the portal (an app’s allowedAuthMethods).

SIWS (Sign-In With Solana) — the player signs a challenge with an existing browser wallet (Phantom, Solflare, Backpack).
Telegram — inside a Telegram Mini App, the player is signed in automatically from the launch data — zero taps.
Email + one-time code — the player enters their email and a 6-digit code we send; no wallet or extension needed. Single-use, short-lived, attempt-capped.

In the SDK these are all on useWalletAuth():

TS
const {
  connectExternal,        // SIWS (pass a wallet adapter)
  connectTelegram,        // Telegram Mini App
  requestEmailOtp,        // email → sends the code
  connectEmail,           // email + code → session
  status, accessToken, logout,
} = useWalletAuth();

// Email flow:
await requestEmailOtp("player@email.com");
await connectEmail("player@email.com", "123456");

The <WalletLogin/> / <WalletLoginModal/> components render all enabled methods for you, including Telegram zero-tap auto-login inside a Mini App.

Identity & captured profile

A player’s wallet address keys off who they authenticate as, so it’s the same across every shared app — one portable identity. Each login method also captures a small profile, refreshed on every sign-in and returned with the verified identity:

Telegram { username, firstName, lastName, photoUrl }
Email{ email }

It surfaces on the session user and on verifySession as identities[].profile — so your backend can greet a player by their Telegram @username or gate staff tooling by an email domain, all from a verified claim (never from the browser).

Sessions

A successful sign-in returns a short-lived access token (the session token) and a longer refresh token. The SDK stores and refreshes them. Your frontend forwards the access token to your backend to prove who the player is.

Server-to-server verification

This is the important part: never trust a wallet address sent from the browser. Your backend confirms the session with the wallet, using your secret key, and gets back the canonical identity. Only then do you mint your own app session (JWT).

TS
import { IAMGameWalletServer } from "@iamgame/wallet-sdk-server";

const wallet = new IAMGameWalletServer({
  secretKey: process.env.IAMGAME_WALLET_SECRET_KEY!,
  baseUrl: "https://api-wallet.iamgame.com/v1",
});

// Your login endpoint receives { sessionToken } from the frontend.
const verified = await wallet.verifySession(sessionToken);
// verified = { userId, appId, environment, authMethod, walletAddress, wallets[], identities[] }

// Optional: confirm a specific claimed address really belongs to this session.
if (claimedAddress && verified.walletAddress !== claimedAddress &&
    !verified.wallets.some((w) => w.status === "active" && w.address === claimedAddress)) {
  throw new Error("wallet does not match the verified session");
}

return signMyAppJwt({ userId: verified.userId, wallet: verified.walletAddress });

verifySession is scoped to your key’s app and environment, and fails closed — an invalid, expired, or out-of-scope session is rejected. Under the hood it calls POST /v1/sessions/verify with your sk_; you can call that endpoint directly if you’re not on Node.

bash
curl -X POST https://api-wallet.iamgame.com/v1/sessions/verify \
  -H "authorization: Bearer $IAMGAME_WALLET_SECRET_KEY" \
  -H "content-type: application/json" \
  -d '{ "sessionToken": "..." }'

This is the same handshake whether the relying party is your own platform or an external studio embedding your games — one wallet identity, verified once, trusted everywhere.