Skip to main content
Sign In With Ethereum (SIWE) lets users authenticate by signing a structured message with their Ethereum wallet. No password. No email. The server verifies the signature came from the claimed address, then creates a session. The standard is EIP-4361. It works with any wallet that supports personal_sign: MetaMask, WalletConnect, Coinbase Wallet, Rainbow, and others.

Setup

1

Add the plugin

lib/kavach.ts
2

Add a signature verifier (production)

Out of the box, the plugin validates message structure and nonce integrity but does not do on-chain secp256k1 recovery. For production, pass a verifySignature function using viem or ethers:
lib/kavach.ts
Without verifySignature, the plugin trusts the address in the message body. Anyone can forge a sign-in by submitting a valid-looking message without a real signature. Always add signature recovery before going to production.

Sign-in flow

SIWE requires three steps: get a nonce, sign a message in the wallet, then submit both to the server.
1

Get a nonce

GET /auth/siwe/nonceRequest a server-generated nonce before building the sign-in message. Nonces are single-use and expire after 5 minutes (configurable).
2

Build and sign the message

Construct the EIP-4361 message string from the user’s address, the nonce, and your app metadata. Pass it to the wallet for signing.
The message the user sees in their wallet looks like:
3

Verify and start a session

POST /auth/siwe/verifySubmit the original message and the wallet signature. On success, the server returns the verified Ethereum address and chain ID. Create a session with your session management layer from here.
Response, 200 OK

Nonce lifecycle

Every sign-in attempt must use a fresh nonce from the server. Nonces:
  • Are 32 random hex bytes (256 bits of entropy)
  • Expire after nonceTtlSeconds (default: 300 seconds)
  • Are deleted immediately after a successful or failed verification, they cannot be reused
The nonce is embedded in the signed message, so it cannot be stripped or replaced after signing. This prevents replay attacks: a captured (message, signature) pair from one session cannot be submitted again. If the nonce expires before the user signs, the verify endpoint returns 400 with "Nonce expired". Request a new nonce and rebuild the message.

Linking wallets to users

SIWE verifies an address, it does not create or look up a user by itself. After a successful /auth/siwe/verify, use the returned address to find an existing user record or create a new one:

Endpoints

Configuration reference

Security considerations

Always add verifySignature in production. Without it, the plugin validates message structure and nonce state, but anyone can submit a well-formed SIWE message for any address without a real signature. Domain and URI binding. The plugin rejects messages where domain or uri do not match the server’s config. This prevents phishing attacks where a malicious site captures a signature meant for a different origin. Nonce reuse prevention. Nonces are deleted on first use regardless of whether verification succeeds. A second attempt with the same nonce always fails. Chain ID. The chain ID in the message is returned to your application but is not enforced by the plugin. If your app is chain-specific (e.g. only Ethereum mainnet), check that chainId === 1 (or your expected value) after verification.
Last modified on April 29, 2026