Open standards toolkit for Sign-In with Ethereum (SIWE), Decentralized Identifiers (DIDs), and W3C Verifiable Credentials. Maintainers of the canonical `siwe` library and `ssi`/`didkit` for VC issuance and verification.
- 01Sign-In with Ethereum (EIP-4361)
- 02Verifiable Credentials issuance + verification
- 03DID-based identity flows
- 04self-sovereign identity wallets
- 05government / mDL deployments
- pnpm add siwe viem
- pnpm add @spruceid/didkit-wasm
| Variable | Scope | Description |
|---|---|---|
| NEXT_PUBLIC_SIWE_DOMAIN | Client | Domain string included in the SIWE message — must match `window.location.host` in production or signature verification fails. |
| SIWE_SESSION_SECRET | Server | Server-only secret used to sign the iron-session/JWT cookie that holds the verified address after successful SIWE verification. |
Use `siwe` for EIP-4361 Sign-In with Ethereum. On the client, fetch a nonce from `/api/siwe/nonce`, build a `new SiweMessage({ domain, address, statement, uri, version: '1', chainId, nonce })`, ask the connected wallet to sign `message.prepareMessage()`, then POST the message + signature to `/api/siwe/verify`. On the server call `await new SiweMessage(payload).verify({ signature, nonce, domain })` and only set the session cookie when `success === true`. For verifiable credentials use `@spruceid/didkit-wasm` (`issueCredential`, `verifyCredential`) with a DID key generated from your issuer's private key, and use `ssi`/DIDKit on the backend to validate VC presentations.
- ⚑`SiweMessage.verify` checks `domain` and `nonce` — if either is missing or stale the call resolves with `success: false` instead of throwing, so always branch on `success` not on a try/catch.
- ⚑Nonces must be single-use and stored server-side (e.g. iron-session, Redis) — reusing the same nonce across requests opens a replay window even though signatures look valid.
- ⚑EIP-4361 messages are case-sensitive on the address — pass the checksummed address from `viem`'s `getAddress()` or hardware wallets that lowercase the address will fail verification.
- ⚑Smart-contract wallets (Safe, Coinbase Smart Wallet, ERC-4337) use ERC-1271 signatures — call `verify({ ..., provider })` with a public client so `siwe` can fall back to the on-chain `isValidSignature` check.
- ⚑DIDKit/ssi WASM blobs are large (>2MB) and must be loaded dynamically (`await import('@spruceid/didkit-wasm')`) inside a server route or worker, never bundled into the client bundle.