How it works
Three steps. No passwords, no cookies, no server-side secret storage. The only thing that proves you own a .btc name is your private key.
Generate nonce challenge
Your site calls createChallenge(). A random 16-byte nonce is generated and embedded in a canonical message with your domain, the user's .btc name, and a timestamp.
Wallet signs with BIP-322
The user's wallet (UniSat, Xverse, etc.) signs the message using BIP-322 simple signing. No transaction is broadcast. No fees. Just a Schnorr signature over the challenge text.
Server verifies ownership
Your backend calls verifySignature(). It checks the Schnorr signature against the P2TR pubkey, then confirms the address matches the on-chain owner of the .btc name via BNRP.
Live demo
Enter a .btc name, generate a challenge, and sign it with your wallet. The signature is verified locally using the SIWB library.
- Install UniSat Wallet or Xverse browser extension.
- Create or import a wallet with a P2TR (Taproot) address.
- Paste your
bc1p...address in the field above and click Generate Challenge. - Then click Connect Wallet & Sign to trigger the wallet's sign prompt.
Message Format
The canonical SIWB message. Plain text. Human-readable. Every field is required except Expiration Time.
| Field | Description | Required |
|---|---|---|
| domain | The domain requesting authentication. Bound to the challenge. Prevents cross-site replay attacks. | Yes |
| Name | The .btc name the user claims to own. Server verifies this resolves to the signing address. | Yes |
| Address | Bitcoin P2TR address (bc1p...). The Schnorr signature is verified against the x-only pubkey extracted from this address. | Yes |
| URI | Full URI of the requesting resource. RFC-3986 compliant. | Yes |
| Version | SIWB spec version. Currently 1. |
Yes |
| Nonce | 32-character hex random nonce (16 bytes). Prevents replay attacks. Single-use server-side. | Yes |
| Issued At | ISO 8601 timestamp. Challenge expires 10 minutes after issuance by default. | Yes |
| Expiration Time | Optional explicit expiry. ISO 8601. If omitted, server enforces a 10-minute window from Issued At. |
No |
| Statement | Human-readable terms. Appears after the blank line. Shown verbatim in wallet signing prompt. | Yes |
Add to your site
Drop in the script tag. The library is under 15KB, pure client-side, no dependencies.
Server verification
Use the same library in Node.js or a Cloudflare Worker. Web Crypto API is available in both environments.
Endpoint spec
The BNRP API exposes two endpoints implementing the server-side SIWB flow. Base URL: https://api.bnrp.name
| Endpoint | Description | Body / Response |
|---|---|---|
| POST /v1/siwb/challenge | Generate a signed challenge for a given address and .btc name. Returns a fresh nonce and canonical message text. Challenge expires in 10 minutes. | Body:{ domain, address, btcName }Response: { message, nonce, expiresAt } |
| POST /v1/siwb/verify | Verify a BIP-322 Schnorr signature against the challenge message and confirm the address matches the on-chain .btc name owner via BNRP resolution. Nonce is invalidated after use. | Body:{ message, signature, address }Response: { ok, name, owner } |