new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Better Auth · Plugins · all subjects

siwe plugin

12 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Sign In With Ethereum plugin for wallet authentication

Better Auth includes a Sign In With Ethereum plugin that provides Ethereum wallet authentication.

SIWE plugin overview and ERC-4361 standard

The Sign in with Ethereum (SIWE) plugin allows users to authenticate using their Ethereum wallets following the ERC-4361 standard. It provides flexibility by allowing implementation of custom message verification and nonce generation logic.

SIWE server plugin import and basic configuration

Import the SIWE plugin with `import { siwe } from "better-auth/plugins"` and add it to the plugins array in betterAuth configuration. The plugin is initialized with `siwe()` and accepts configuration options for domain, emailDomainName, anonymous, getNonce, verifyMessage, and ensLookup.

SIWE client plugin import and usage

Import the SIWE client plugin with `import { siweClient } from "better-auth/client/plugins"` and add it to the plugins array in createAuthClient. The client plugin is initialized with `siweClient()` and does not require any configuration options.

SIWE server configuration options

The SIWE plugin configuration accepts these options: domain (required, domain name of the application for SIWE message generation), emailDomainName (optional, email domain for creating user accounts in non-anonymous mode, defaults to domain from base URL), anonymous (optional, whether to allow anonymous sign-ins without email, defaults to true), getNonce (required function, returns Promise<string> with cryptographically secure ERC-4361 nonce of 8-250 alphanumeric characters), verifyMessage (required function, returns Promise<boolean> for signature verification), and ensLookup (optional function, looks up ENS names and avatars for Ethereum addresses).

SIWE nonce generation client method

Call `authClient.siwe.nonce()` to generate a nonce before wallet signing. Returns an object with data containing the nonce value. The nonce is not bound to a wallet address or Chain ID because one-step wallet flows may not know either value until the wallet signs the SIWE message.

SIWE verify client method

Call `authClient.siwe.verify()` with an object containing: message (required, valid ERC-4361 SIWE message string), signature (required, signature from user's wallet in format 0x...), and email (optional, required if anonymous is false). Returns an object with data containing the authenticated user on success.

SIWE message validation and nonce binding

Before accepting a signature, the SIWE plugin parses the ERC-4361 message, consumes the matching server-issued nonce, derives the address and Chain ID from the signed message, requires the signed domain to match the configured domain, and honors the message's Expiration Time and Not Before bounds. Signature recovery alone is not sufficient. If any signed field is invalid, verification fails with a 401 error (UNAUTHORIZED_SIWE_MESSAGE_MISMATCH).

SIWE email handling and account creation

A SIWE signature proves control of the wallet, not ownership of the email passed. The plugin stores the email unverified and only binds it to the new account when it is not already in use. When anonymous is false and the supplied email already belongs to another account, the new wallet account is created with a wallet-derived address instead, preventing a sign-in from attaching an email another account owns.

SIWE chain-specific message handling

Chain selection belongs in the ERC-4361 message itself, not in the verification request body. The workflow is: generate a nonce, build the SIWE message with the wallet address and target Chain ID, ask the wallet to sign that exact message, then verify the signed message. The signed SIWE message must include a positive Chain ID, and verification derives wallet identity from that signed Chain ID, failing with a 401 error if the message is missing a valid Chain ID.

SIWE walletAddress table schema

The SIWE plugin adds a walletAddress table with the following fields: id (string, primary key), userId (string, reference to user.id), address (string, Ethereum wallet address), chainId (number, Chain ID such as 1 for Ethereum mainnet), isPrimary (boolean, whether this is the user's primary wallet), and createdAt (date, creation timestamp).

SIWE complete implementation example with viem

Complete SIWE implementation using viem for signature verification and ENS lookup: import betterAuth from better-auth, siwe from better-auth/plugins, generateRandomString from better-auth/crypto, verifyMessage and createPublicClient from viem, and mainnet from viem/chains. Configure siwe with domain and emailDomainName, getNonce returning generateRandomString(32, "a-z", "A-Z", "0-9"), verifyMessage using viem's verifyMessage function with address, message, and signature parameters, and ensLookup using viem's createPublicClient with mainnet chain to call getEnsName and getEnsAvatar methods. The ensLookup should return an object with name and avatar properties, falling back to walletAddress and empty string if ENS lookup fails.

Give your agent this brain