Auth platform with first-class crypto wallet login alongside email magic links, passkeys, OAuth, SSO, and SCIM. Supports Sign-In with Ethereum (SIWE) for EVM wallets and message-signing flows for Solana wallets.
- 01Web2 + Web3 hybrid auth
- 02SIWE with phishing-resistant domain binding
- 03passkeys + crypto wallet in one session
- 04B2B apps needing SSO + wallet login
- 05drop-in UI for wallet auth
- pnpm add @stytch/nextjs @stytch/vanilla-js
- pnpm add stytch
| Variable | Scope | Description |
|---|---|---|
| NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN | Client | Stytch frontend public token from the Stytch dashboard. Client-safe. |
| STYTCH_PROJECT_ID | Server | Stytch project ID (e.g. `project-live-...`). Server-only. |
| STYTCH_SECRET | Server | Stytch project secret used by the Node SDK to call the backend API. Server-only. |
Use Stytch for crypto-wallet auth alongside email/passkey login. On the client, call `stytch.cryptoWallets.authenticateStart({ crypto_wallet_type: 'ethereum', crypto_wallet_address: address, siwe_params: { domain, statement, uri } })` to receive a SIWE-formatted challenge, ask the connected wallet to sign it (e.g. `walletClient.signMessage` from viem), then call `stytch.cryptoWallets.authenticate({ crypto_wallet_address, signature })` to exchange the signature for a Stytch session token. Wrap the Next.js app in `<StytchProvider stytch={createStytchUIClient(token)}>` and gate routes via `useStytchUser()` / `useStytchSession()`. On the server, verify the session JWT with `stytch.sessions.authenticateJwtLocal(jwt)` from the `stytch` Node SDK before trusting any `crypto_wallet_address` claim.
- ⚑`siwe_params` are required for Ethereum flows that use Sign-In with Ethereum — omitting them falls back to plain message signing without domain binding, defeating the anti-phishing guarantee.
- ⚑Solana wallets use a different `crypto_wallet_type: 'solana'` and a different signing path (ed25519 over the raw challenge bytes) — passing `siwe_params` on a Solana flow throws a 400.
- ⚑The frontend SDK exposes a session token, but server routes must call `authenticateJwtLocal` (or `sessions.authenticate` for revocation checks) — never trust the address embedded in the JWT until the signature is locally verified.
- ⚑Stytch sessions roll over: the JWT in localStorage may be older than the live session — refresh it via `stytch.session.authenticate({ session_duration_minutes })` on app focus or downstream calls will 401 intermittently.
- ⚑Stytch B2B and Consumer projects are separate — an SDK token for a Consumer project will silently fail against B2B endpoints (and vice-versa); pick one project type per app.