← Protocols
Spruce ID
Wallet / Auth·EVM · Multi-chain

Spruce ID

01Description

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.

02Best for
  • 01Sign-In with Ethereum (EIP-4361)
  • 02Verifiable Credentials issuance + verification
  • 03DID-based identity flows
  • 04self-sovereign identity wallets
  • 05government / mDL deployments
03Install
  • pnpm add siwe viem
  • pnpm add @spruceid/didkit-wasm
04Environment variables
VariableScopeDescription
NEXT_PUBLIC_SIWE_DOMAINClientDomain string included in the SIWE message — must match `window.location.host` in production or signature verification fails.
SIWE_SESSION_SECRETServerServer-only secret used to sign the iron-session/JWT cookie that holds the verified address after successful SIWE verification.
05Prompt snippet
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.
06Gotchas
  • `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.
07Alternatives