Quickstart
Sign users in with one tap of their Revibase accessory. The tap proves they hold it, and the accessory's on-chain record names the wallet linked to it, so you sign them in as that wallet. No wallet popup. The accessory can't sign transactions or move funds.
pnpm add phygital-token-sdk @solana/kit
You need HTTPS and a Solana RPC URL. Issue a short-lived challenge per attempt and consume it atomically after verification, before creating a session.
1. Issue a challenge (server)
const message = crypto.randomUUID(); // store it, and accept it once
2. Tap (browser)
import { createSolanaRpc } from "@solana/kit"; import { startAuthentication } from "phygital-token-sdk"; const rpc = createSolanaRpc(SOLANA_RPC_URL); const response = await startAuthentication(message, { rpc }); // Send `response` to your server.
For a native app or kiosk reader, pass { transceive } instead of running it in a browser.
3. Verify and sign in (server)
import { address, createSolanaRpc } from "@solana/kit"; import { fetchMaybePhygitalToken, findPhygitalTokenPda, PhygitalTokenType, verifyResponse, } from "phygital-token-sdk"; const rpc = createSolanaRpc(SOLANA_RPC_URL); const UNLINKED = address("11111111111111111111111111111111"); const ACCEPTED = new Set([ PhygitalTokenType.Controlled, PhygitalTokenType.Permanent, PhygitalTokenType.Bearer, ]); const { isVerified, secp256r1PublicKey } = verifyResponse({ expectedMessage: message, response, }); if (!isVerified) throw new Error("Bad tap"); const token = await fetchMaybePhygitalToken( rpc, await findPhygitalTokenPda(secp256r1PublicKey), ); if (!token.exists) throw new Error("Not a Revibase accessory"); if (!ACCEPTED.has(token.data.tokenType)) throw new Error("Accessory type not accepted"); if (token.data.linkedWallet === UNLINKED) throw new Error("Link a wallet to this accessory first"); // Sign the user in as token.data.linkedWallet.
Which accessories to accept
All three sign the user in as the linked wallet. They differ in how that link can change:
| Type | Linked wallet |
|---|---|
Controlled | Requires unlinking by the linked wallet before relinking |
Permanent | Fixed for good |
Bearer | The holder can relink it while unlocked |
Token type controls wallet linkage, not whether a card can be traded. NFT cards can use any type; trading a card does not automatically update its wallet link or transfer its NFT.
To allow only some types, remove the others from ACCEPTED.
Errors
The SDK throws PhygitalTokenError. Branch on code; message is safe to show.
| Code or symptom | What to do |
|---|---|
AUTHENTICATION_CANCELLED | The user dismissed the prompt. Do nothing. |
WEBAUTHN_UNSUPPORTED | This browser can't tap. Offer your wallet login instead. |
MESSAGE_MISMATCH | The tap answered an old challenge. Issue a fresh one per attempt. |
| Tap never finishes | Serve over HTTPS and pass { rpc } |
| Accessory isn't linked | The owner links it at portal.revibase.com, then taps again |
Next
- Gate access: allow by wallet, accessory or NFT
- Verify on-chain: require a tap inside your Solana program