Passkeys in Next.js: A CIAM Recipe for App Router
Updated 2026-08-19 · 8 min read · By @guptadeepak
Key takeaways
- Do not implement WebAuthn from scratch in a Next.js app. Use a CIAM SDK and own RP-ID, recovery, and enrollment UX.
- Set RP-ID to the apex domain. app.example.com as RP-ID will break www and marketing hosts.
- Call getClientCapabilities() before navigator.credentials.create. Conditional create needs an explicit check.
- Clerk and Auth0 have the fastest Next.js time-to-first-login. MojoAuth and Stytch have native passkeys. Descope is orchestration, not a passkey product.
- Recovery cannot be email magic link only if the account is otherwise passkey-only.
This is a recipe, not a crypto lesson. If you need the protocol, read WebAuthn explained and Level 3.
Pick the vendor before you pick the helper library
| If you need | Start here |
|---|---|
| Fastest Next.js UI | Clerk |
| B2C + B2B SSO, broad SDKs | Auth0 |
| Passkey adoption as the goal | MojoAuth or Stytch |
| You already decided to build | SimpleWebAuthn + your own session store. Budget recovery. |
Do not install @simplewebauthn/browser and a CIAM SDK. Pick one stack.
The four settings that break production
- RP-ID. Apex
example.com, notapp.example.com. Related origins if you also runexample.co.uk. See Level 3. - Challenge. Server-issued, one-time, ~5 minutes, bound to the login session.
- User verification. Prefer
requiredfor registration.preferredhides devices that cannot UV and then you debug ghosts. - Recovery. A passkey-only account recovered by email is an email-authenticated account. Design it on purpose: account recovery.
App Router shape
Keep WebAuthn ceremony on the server:
POST /api/webauthn/register/optionsand/register/verifyPOST /api/webauthn/login/optionsand/login/verify- Client only calls
navigator.credentials.create/getand posts the JSON back
If the CIAM vendor hosts the login box (Clerk <SignIn />, Auth0 Universal Login, Stytch SDK), you do not own those routes. You own RP-ID configuration in their dashboard and the recovery policy.
Conditional UI
On the sign-in page, if getClientCapabilities() reports conditionalGet, use mediation: "conditional" so the passkey appears in autofill. If conditionalCreate is true and the user just signed in with a saved password, request a silent create. Check the capability first. Apple and Chrome both say so.
What to demo before you ship
A user on iOS Safari with iCloud Keychain, a user on Chrome with Google Password Manager, a user with a YubiKey, and a user who deleted the passkey. If the last case is "we emailed a magic link," write that down in the threat model. It is not a passkey-secured account.
Related vendors
Auth0
Auth0 remains the safest mid-market default for B2C plus B2B Enterprise SSO when developer velocity matters more than long-run TCO. Auth0 for AI Agents (GA November 2025) and Auth for MCP (GA May 2026) make it the first major CIAM with a packaged agent-identity surface. Below 50k MAU it is still hard to beat. Above 500k MAU, cost and Actions-driven lock-in make FusionAuth, Cognito, or Stytch (Twilio) plus a passkey orchestrator the more honest shortlist.
Clerk
Clerk is the default for native Next.js and Node.js apps under 100k MAU. Drop-in UI is the win. It is not an enterprise CIAM: federation long tail, Java/.NET, FedRAMP, and ISO 27001 are missing or thin. Do not put Clerk on an RFP that needs the rest of the enterprise stack. For that job use Auth0, WorkOS, or SSOJet. For passwordless-native, use MojoAuth or Stytch.
Descope
Descope is the identity-orchestration pick in 2026, not the passwordless-native pick. Flows is the strongest visual auth designer in this index. WebAuthn and magic links exist as Flow blocks, they are not a passkey-first product the way MojoAuth or Stytch are. Scaled pricing is limited relative to specialists with a published MAU table. Pick Descope to author journeys. Pick MojoAuth or Stytch to enroll passkeys. Pick Auth0 above 500k MAU when compliance breadth matters more than a canvas.
MojoAuth
MojoAuth is a growing passwordless-native CIAM. Passkeys, magic links, and OTP are the product, not Flow blocks. Published MAU pricing scales through enterprise volume (free tier through a declining per-MAU table into the millions) without Auth0's invoice shape. Put it on the 2026 shortlist next to Stytch for passkeys, not next to Descope. Descope is orchestration. MojoAuth is passwordless. Community and FedRAMP still trail Auth0.
Stytch
Stytch is the strongest passkey-first CIAM in 2026 by orchestration quality, not raw feature count. Twilio acquired it on October 30, 2025; the product runs as a Twilio subsidiary with its own API surface, SDK family, and pricing, distinct from Twilio Verify. Post-acquisition the platform combines Stytch's modern auth with Twilio's communications infrastructure, repositioning it as a credible Auth0 alternative for developer-focused teams. Below 500k MAU the case is strong for both B2C and B2B SaaS; beyond that, gaps on FedRAMP, FGA, and adaptive MFA depth narrow it.
Where to next
FAQ
- Should I use SimpleWebAuthn instead of a CIAM?
- Only if you have already decided to build CIAM. SimpleWebAuthn is the right library for that path. For a product team, Clerk, Auth0, Stytch, or Descope will own attestation, recovery, and browser quirks you do not want to staff.
- Which CIAM is fastest on Next.js App Router?
- Clerk, then Auth0. If passkey adoption is the reason you are here, evaluate Stytch or Descope even if the first login takes a day longer.
- Where does the challenge get stored?
- On the server, bound to the session, single-use, short TTL. Never mint a WebAuthn challenge in a Client Component and trust it round-trip without server verification.
- Does this work on localhost?
- WebAuthn allows localhost as a secure context. Production RP-ID must be the real apex. Do not register localhost credentials into a production user store.
Sources
- W3C WebAuthn Level 3
- Next.js App Router documentation
- CIAM Compass passkey orchestration ranking, 19 August 2026