Skip to content

Sign-In with Ethereum

Sign-In with Ethereum (SIWE, ERC-4361) wallet authentication. You supply nonce generation and signature verification; the plugin owns message parsing and the session half. Mirrors the TS siwe() plugin.

Enable

python
from better_auth import BetterAuth
from better_auth.plugins_ext import SiwePlugin

async def get_nonce():
    ...  # return a fresh nonce string

async def verify_message(args):
    ...  # {"message", "signature", "address", "chainId", ...} -> bool

auth = BetterAuth(
    secret="a-strong-32-character-minimum-secret",
    plugins=[
        SiwePlugin(
            domain="example.com", get_nonce=get_nonce, verify_message=verify_message
        )
    ],
)

Options

OptionTypeDefaultDescription
domainstrrequiredThe domain the SIWE message must be bound to.
get_noncecallablerequired() -> str, generates a nonce.
verify_messagecallablerequired(dict) -> bool, recovers/checks the secp256k1 signature (bring your own web3 library).
email_domain_namestr | NoneNone (origin of base_url)Domain for the generated placeholder email.
anonymousboolTrueAllow wallet-only accounts; False requires an email in the verify body.
ens_lookupcallable | NoneNoneResolve ENS name/avatar for new users.

Endpoints

MethodPath
POST/siwe/nonce
POST/siwe/get-nonce (alias)
POST/siwe/verify

Schema

TableColumns
walletAddressuserId, address, chainId, isPrimary, createdAt

Notes

  • The plugin ships its own ERC-4361 message parser (ported verbatim from TS parse-message.ts) — it does not trust verify_message for message-body validation; your callable only has to check the signature.
  • Addresses are EIP-55 checksummed via keccak256 (pycryptodome); hashlib's sha3_256 is FIPS-202 SHA3 and cannot be used for this.

MIT licensed · API-compatible with better-auth