Server Setup
Configure the SIWP plugin on your server with Better Auth.
Prerequisites
- A Better Auth instance (setup guide)
- A database adapter configured with Better Auth (Drizzle, Prisma, etc.)
better-authv1.5.0 or higher
Installation
npm i @zig-zag/better-siwp better-auth zodBasic Setup
Add the siwp() plugin to your Better Auth config:
import { betterAuth } from 'better-auth';
import { siwp } from '@zig-zag/better-siwp';
export const auth = betterAuth({
database: yourAdapter,
plugins: [
siwp({
domain: 'example.com',
}),
],
});Configuration Options
The siwp() plugin accepts an optional config object:
siwp({
// Optional: your domain without protocol
// If not set, auto-detected from the request Origin header
domain: 'example.com',
// Optional: nonce lifetime in seconds (default: 900 = 15 minutes)
nonceExpiresIn: 600, // 10 minutes
// Optional: custom nonce generation
getNonce: async () => {
return crypto.randomUUID();
},
// Optional: custom signature verification
verifyMessage: async ({ message, signature, address }) => {
// Your custom logic — return true if valid
return true;
},
// Optional: extract user info from the wallet
getUserInfo: async ({ message, address, signature }) => {
return {
name: 'Alice',
email: 'alice@example.com',
image: 'https://example.com/avatar.png',
};
},
// Optional: domain for generated email addresses
// Default: uses the resolved domain (e.g., "5Grw...@example.com")
emailDomainName: 'users.example.com',
})Options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
domain | string | No | Auto-detected from Origin or Host header | Domain for the SIWS message |
nonceExpiresIn | number | No | 900 (15 minutes, in seconds) | Nonce lifetime in seconds |
getNonce | () => Promise<string> | No | Random alphanumeric string | Custom nonce generation |
verifyMessage | (params) => Promise<boolean> | No | verifySIWS from @talismn/siws | Custom signature verification |
getUserInfo | (params) => Promise<UserInfo> | No | Name from truncated address | Extract user info from the wallet |
emailDomainName | string | No | Same as resolved domain | Domain for generated email addresses |
When your API and frontend run on different ports (e.g., API on 3001, frontend on 3000), the domain is auto-detected from the
Originheader sent by the browser. If you need explicit control, set thedomainoption.
Server Exports
import { siwp, parseMessage, verifySIWS } from '@zig-zag/better-siwp';
import type { SIWPOptions, SiwsMessage } from '@zig-zag/better-siwp';| Export | Description |
|---|---|
siwp(options) | Better Auth server plugin |
parseMessage(message) | Parse a SIWS message string (re-exported from @talismn/siws) |
verifySIWS(message, signature, address) | Verify a SIWS signature (re-exported from @talismn/siws) |
SIWPOptions | Plugin configuration type |
SiwsMessage | SIWS message type |
API Routes
The plugin registers two endpoints on your Better Auth instance. Both use POST:
| Endpoint | Method | Body | Response |
|---|---|---|---|
/api/auth/siwp/nonce | POST | { walletAddress: string } | { nonce: string } |
/api/auth/siwp/verify | POST | { message: string, signature: string, walletAddress: string } | { token: string, success: boolean, user: { id: string, walletAddress: string } } |
Next.js App Router
import { auth } from '@/lib/auth';
import { toNextJsHandler } from 'better-auth/next-js';
export const { GET, POST } = toNextJsHandler(auth);