Skip to Content
Verse8 PlatformAuth Helpers

Auth Helpers

@verse8/platform exposes a small Verse8 namespace that parses and cryptographically verifies the ?auth=<token> credential the Verse8 host injects into game iframes. Use it when you need to read the current user’s identity directly — for example, to display “Welcome, 0x123…” in a header, or to read the user’s verseId for a non-VXShop API call.

VXShop.init() already consumes this token internally; you don’t need to plumb these helpers into it manually. The helpers are here for the cases where you want the values for your own UI / logic.

Install

The same Verse8 namespace ships across all three entry points. Pick the one that matches your stack.

React app (default entry)

import { Verse8 } from "@verse8/platform";

Vanilla / non-React (bundler)

import { Verse8 } from "@verse8/platform/vanilla";

Script tag (no build step)

<script src="https://unpkg.com/@verse8/platform/dist/index.global.js"></script> <script> // `Verse8` is attached to `window` after the script loads — // inline / deferred / module scripts that follow can use it directly. const user = Verse8.getUser(); console.log(user.account); </script>

A jsdelivr mirror is also published — swap unpkg.com for cdn.jsdelivr.net/npm/... if preferred. Pin a version (e.g. @verse8/[email protected]) in production so a future release can’t shift the bundle under you.

API

const Verse8 = { /** * Read `?auth=` from `window.location.search`, verify, and return the * parsed user credential. Throws when missing / malformed / expired / * signature mismatch. */ getUser(opts?: ParseAuthTokenOptions): UserCredential; /** * Verify + parse a raw authToken string (no URL access). Useful for * server-side verification, tests, or scenarios where the token comes * from somewhere other than the page URL. */ parseAuthToken(token: string, opts?: ParseAuthTokenOptions): UserCredential; }; interface UserCredential { verse: string; account: `0x${string}`; // primary Verse8 EOA exp: number; // unix seconds sub?: { tier: string; exp: number } | null; // server-signed only follow?: boolean; // server-signed only externalWallet?: { // server-signed only type: "abstractSmartWallet"; address: string; // e.g. user's Abstract Global Wallet } | null; } interface ParseAuthTokenOptions { /** Override the trusted Verse8 signer (defaults to the production address). */ trustedSigner?: `0x${string}` | string; /** Skip the `exp` check (for tooling that replays historical tokens). */ ignoreExpiration?: boolean; /** * Reject self-signed (guest) tokens — accept only tokens signed by * the trusted Verse8 signer, i.e. only *logged-in* users. Useful for * surfaces that should only run for users with a Verse8 account. */ requireTrustedSigner?: boolean; }

Both helpers cache the verified credential per authToken string — calling them repeatedly is cheap (signature recovery is the expensive step). When the URL changes (different ?auth=), the cache invalidates on the next call.

Verification model

The SDK accepts a token if the recovered signing address matches either:

  1. The Verse8 production signer — the logged-in path. The Verse8 host issues the token from its server-side signing key after the user signs in. Server-only fields (sub, follow, externalWallet) are populated on this path.
  2. The credential’s own account — the guest / anonymous path. Users who haven’t signed in get a client-generated wallet; the client signs the credential JSON with that wallet, proving ownership of the address that appears as account.

Both paths produce a verified account you can trust. They differ on which identity account represents:

  • Logged-in useraccount is the Verse8 EOA the user signed in with. The Verse8 server attests to it by signing the credential.
  • Guest useraccount is the address of the wallet the client generated. The client attests to it by signing the credential. Different guest sessions get different accounts; there is no external identity behind it.

A page setting its own ?auth= can produce a valid self-signed token, but only for an address whose private key it controls. It cannot claim another user’s account without that user’s key. So account is always a real key holder — only the logged-in path also ties account to a Verse8 user identity.

Errors

Both helpers throw when verification fails. The error message begins with [Verse8] and identifies the cause:

CauseMessage contains
No ?auth= query parameter (getUser only)no auth query parameter found
?auth= value is not valid base64is not valid base64
Decoded payload is not valid JSONis not valid JSON
Outer envelope missing msg or signaturemissing msg or signature
Inner credential missing verse / account / expmissing required fields
exp is in the pastis expired
Signature doesn’t match trusted signer OR accountsignature verification failed
Signature byte length wrong / non-hexsignature verification failed: ...

Wrap in try/catch:

let user: UserCredential | null = null; try { user = Verse8.getUser(); } catch (err) { console.error("[Verse8] no usable auth:", err.message); // e.g. show a "please reload from the Verse8 portal" prompt }

Examples

Display the current user

import { Verse8 } from "@verse8/platform/vanilla"; const headerEl = document.querySelector("#user-account"); try { headerEl.textContent = Verse8.getUser().account; } catch { headerEl.textContent = "guest"; }

Script tag (no build step)

Plain HTML page, Unity WebGL host page, single-file prototype — load the global build from a CDN and read window.Verse8 directly:

<!doctype html> <span id="user-account">guest</span> <script src="https://unpkg.com/@verse8/platform/dist/index.global.js"></script> <script> try { const user = Verse8.getUser(); document.getElementById("user-account").textContent = user.account; } catch (err) { console.error("[Verse8]", err.message); } </script>

Verse8 becomes available synchronously when the global script finishes executing, so any inline <script> after it can call Verse8.getUser() immediately. The global build also installs window.VXShop from the same bundle — you don’t need a separate script tag for VXShop.

React

import { Verse8 } from "@verse8/platform"; import { useMemo } from "react"; function CurrentUser() { const user = useMemo(() => { try { return Verse8.getUser(); } catch { return null; } }, []); return <span>{user?.account ?? "guest"}</span>; }

Inspect the full credential

const user = Verse8.getUser(); console.log(user.verse, user.account, new Date(user.exp * 1000)); if (user.sub) { // Subscription info — only present on server-signed tokens. console.log("subscription tier:", user.sub.tier); } if (user.externalWallet) { // Secondary on-chain identity (e.g. Abstract Global Wallet). // `account` stays the primary Verse8 EOA — externalWallet.address // is for games that need the user's external chain identity. console.log("external wallet:", user.externalWallet.address); }

Require a logged-in user (reject guests)

By default both paths are accepted — guest sessions get a valid account, just without a tie to a Verse8 user identity. UX surfaces that should only run for logged-in Verse8 users (anything reading sub / follow / externalWallet, or granting entitlements tied to a Verse8 account such as VXShop history) opt in to strict mode:

const user = Verse8.getUser({ requireTrustedSigner: true });

When requireTrustedSigner is true, only tokens signed by the trusted Verse8 signer pass; self-signed (guest) tokens throw with [Verse8] signature verification failed: recovered signer ... is not the trusted Verse8 signer .... Catch it and route the user to a sign-in prompt:

try { const user = Verse8.getUser({ requireTrustedSigner: true }); showLoggedInUI(user); } catch { showLoginPrompt(); }

Verify a token from a non-URL source

parseAuthToken lets you verify a token string directly — for example, one received via postMessage, a WebSocket frame, or your own server handing it back to the client:

import { Verse8 } from "@verse8/platform/vanilla"; window.addEventListener("message", (event) => { if (event.data?.type !== "AUTH_REFRESH") return; try { const user = Verse8.parseAuthToken(event.data.token); console.log("refreshed user:", user.account); } catch (err) { console.error("[Verse8] rejected refreshed token:", err); } });

Custom trusted signer (staging, custom verse)

const user = Verse8.getUser({ trustedSigner: "0xMyCustomVerse8SignerAddress...", });

Token Format

For reference, the ?auth= token is base64(JSON.stringify({ msg, signature })), where msg is either:

  • The raw JSON { verse, account, exp, ... }, or
  • A base64 encoding of that JSON (the SDK handles both variants).

signature is a 65-byte hex string (0x + 130 hex chars) from EIP-191 personal_sign over msg. The SDK recovers the signing address from the signature and accepts the token only if it matches the trusted Verse8 signer or the credential’s own account.

Bundle cost — signature verification adds ~7.3 KB gzipped to the script-tag bundle (via @noble/secp256k1 + @noble/hashes). The ESM / CJS entries leave both as peer deps so consumers’ bundlers can share copies and tree-shake.

Last updated on