Auth Overview

Turn anonymous wallets into customer accounts
View as Markdown

Why auth matters

Without authentication, every agent payment is a one-off event. A wallet pays, you fulfill, and then it’s gone — no identity, no session, no way to know if this agent has paid before or what it’s entitled to.

Gwop Auth changes that. A single dust payment ($0.001 USDC) proves wallet ownership and creates a persistent customer account. First auth is signup. Repeat auth is login. No registration form, no email, no password — the payment is the proof.

Once authenticated, the merchant has everything a software business needs: identity, sessions, entitlements, account history, and plan enforcement. The agent goes from an anonymous wallet to a known customer.

How it works

  1. Your backend creates an auth challenge — Gwop returns an auth_intent_id with x402 payment URLs for a $0.001 USDC dust charge on Base and Solana.
  2. You hand the challenge to the agent — the agent pays the dust amount via x402 on whichever chain it prefers, proving it controls the wallet.
  3. Your backend exchanges for a JWT — once the dust payment settles, exchange the auth_intent_id for an RS256-signed JWT containing the agent’s identity.
  4. You use the JWT for all subsequent requests — the agent is now an authenticated customer. Verify JWTs locally using Gwop’s public JWKS.
Your Backend Gwop Agent
│ │ │
│── create auth intent ────>│ │
│<── intent + payment URLs ─│ │
│ │ │
│── hand challenge to agent ──────────────────────────────>│
│ │ │
│ │<── $0.001 x402 payment ─────│
│ │── verify on-chain │
│ │ │
│── exchange for JWT ──────>│ │
│<── access_token + identity│ │
│ │ │
│── authenticated requests ───────────────────────────────>│

Agents never talk to Gwop directly. Your backend mediates every step — creating challenges, exchanging tokens, verifying JWTs. This is the Auth0 model applied to wallet identity.

What auth unlocks

  • Identityprincipal.sub is the agent’s permanent customer ID in {chain}:{address} format (e.g. base:0x90c0...953c). Every purchase, session, and credit balance is scoped to this identity.
  • Automatic account creation — first auth from a wallet creates the account (is_new_account: true). Repeat auth from the same wallet returns the existing account (is_new_account: false). No signup endpoint needed.
  • Sessions — the JWT includes a sid (session ID) and an expiry. Sessions are short-lived and revocable. See Sessions.
  • Entitlements — once you know who the agent is, you can enforce plans, credit limits, daily caps, and tier access in your backend.

Wallet identity

The JWT sub claim is the agent’s identity in {chain}:{address} format:

  • base:0x742d35Cc6634C0532925a3b844Bc9e7595f5bA16
  • solana:7sSi2XK9pJuqMV9p4Lz3kxBRtxYRPcC5Yp7CYGkaFqJ

Your backend parses this to extract the chain and wallet address:

1const [chain, address] = token.principal.sub.split(":");
2// chain = "base", address = "0x742d..."

The wallet used to authenticate defines the account. Credits, purchases, and history are all scoped to principal.sub. A different wallet creates a different account — there is no way to merge identities across wallets.

The dust challenge

Auth challenges cost $0.001 USDC (1000 atomic units). This is enough to prove wallet ownership without meaningful cost to the agent. The amount is fixed by the backend and cannot be customized.

The challenge is itself a Gwop invoice — it uses the same x402 payment infrastructure, the same multichain support, and the same on-chain verification. Auth is built on top of the invoice primitive.

Full flow

1import { Gwop } from "@gwop/sdk";
2
3const gwop = new Gwop({
4 merchantApiKey: process.env.GWOP_MERCHANT_API_KEY,
5});
6
7// 1. Create an auth challenge
8const { result: intent } = await gwop.authIntents.create({
9 idempotencyKey: crypto.randomUUID(),
10});
11
12// 2. Hand payment methods to the agent
13// The agent pays the dust challenge via x402 on Base or Solana
14const paymentUrl = intent.challenge.paymentMethods[0].paymentUrl;
15
16// 3. After payment, exchange for JWT
17const { result: token } = await gwop.authIntents.exchange({
18 authIntentId: intent.authIntentId,
19 idempotencyKey: crypto.randomUUID(),
20});
21
22// The agent is now a known customer
23console.log(token.accessToken); // RS256-signed JWT
24console.log(token.principal.sub); // "base:0x90c0..." — permanent identity
25console.log(token.account.isNewAccount); // true on first auth, false on repeat
26
27// 4. Verify JWTs locally using Gwop's public JWKS
28const { result: jwks } = await gwop.auth.getJwks();
29// Use `jose` or any JWT library with these keys

Next steps