Skip to content

fix(webauthn): upgrade to SimpleWebAuthn v13.3+/v14 without breaking stored credentials - #13500

Open
rvdlaar wants to merge 6 commits into
nextauthjs:mainfrom
rvdlaar:fix/simplewebauthn-v13-v14
Open

rvdlaar wants to merge 6 commits into
nextauthjs:mainfrom
rvdlaar:fix/simplewebauthn-v13-v14

Conversation

@rvdlaar

@rvdlaar rvdlaar commented Sep 24, 2026 •

Copy link
Copy Markdown

☕️ Reasoning

Moves WebAuthn/Passkey support from SimpleWebAuthn v9 to v13.3+ / v14. This clears two public SimpleWebAuthn advisories that Dependabot reports for every Auth.js app using passkeys:

Both advisories are already published by SimpleWebAuthn, so nothing new is disclosed here. The PR builds on @rastislavcore's #13378, whose four commits are kept with their authorship, and completes it:

  1. Stored credential IDs keep their format (bug fix vs Update/passkey 01 #13378). Auth.js stores credential IDs as standard base64 and normalises the browser's ID to that format before getAuthenticator(). SimpleWebAuthn v13+ uses base64url, and Update/passkey 01 #13378 stored credential.id as-is, so a passkey registered after the upgrade could never sign in. This PR keeps the stored format and converts only at the SimpleWebAuthn boundary. No adapter or data migration is needed.
  2. next-auth and @auth/sveltekit get their peer ranges updated (Update/passkey 01 #13378 left them at ^9, which fails npm install with ERESOLVE) and their client helpers moved to the { optionsJSON } call form.
  3. The default sign-in page script moves to v13.3.0, the docs cover v13.3+/v14, and the nextjs example uses ^14.0.2.

Review guide

  • Core logic: packages/core/src/lib/utils/webauthn-utils.ts. Two small helpers (base64ToBase64URL, base64URLToBase64) are used at five call sites; the rest of the migration comes from Update/passkey 01 #13378.
  • Clients: packages/next-auth/src/webauthn.ts and packages/frameworks-sveltekit/src/lib/webauthn.ts, a one-line change each.
  • pnpm-lock.yaml: only SimpleWebAuthn's own dependency tree changes. @peculiar/* ASN.1/X.509 packages and tsyringe are added; asn1js, cross-fetch and @simplewebauthn/types are removed. Nothing else is re-resolved. The Socket report on this PR shows improved scores for both packages and no alerts.

Impact for users (release-note draft)

  • Install @simplewebauthn/server@^14.0.2 and @simplewebauthn/browser@^14.0.0 (Node 22+). On Node 20, use @^13.3.2 / @^13.3.0. Both ranges are allowed.
  • Existing Authenticator records keep working; no database change is needed.
  • Custom provider options: nothing was removed from registrationOptions, authenticationOptions, verifyRegistrationOptions or verifyAuthenticationOptions. The one narrowing: attestationType no longer accepts "indirect" (SimpleWebAuthn v13.0.0 dropped it).
  • Custom sign-in pages that call @simplewebauthn/browser directly should use startRegistration({ optionsJSON }) / startAuthentication({ optionsJSON }). The old positional form still works but logs a deprecation warning.

WebAuthn is behind experimental.enableWebAuthn, so I believe a fix: release is appropriate. I'm happy to change it to feat: if you prefer.

🧢 Checklist

  • Documentation (JSDoc + docs pages)
  • Tests
  • Ready to be merged

Unit tests (@auth/core): 162/162. The fixture credential IDs were hex, which reads the same in base64 and base64url, and that is why #13378's encoding bug passed. They now contain +, / and =, the browser response uses base64url, and two tests cover the helpers. @auth/core, next-auth and @auth/sveltekit build (svelte-check reports 0 errors), and their tests pass. The devDependencies use v14, so the repo itself has no open advisory. The floor of the range (@simplewebauthn/server@13.3.3, @simplewebauthn/browser@13.3.0) also builds and passes the same tests. prettier --check and eslint report 0 errors on the changed files.

Real WebAuthn ceremonies (Chromium virtual authenticator, CTAP2 resident key) against a Next.js 16 app using next-auth@5 + Passkey + a custom adapter. Packed builds of this branch were compared with the released packages:

Scenario Result
Released @auth/core 0.41.3 + SimpleWebAuthn 9: register → sign in (control) 6/6
This PR + SimpleWebAuthn 14: register → sign in 10/10
Upgrade: passkey registered on the released version, then sign in on this PR 4/4: same row, counter advances
#13378 as-is + SimpleWebAuthn 13.3: register → sign in 0/2: WebAuthn authenticator not found in database

Suggested squash commit message

fix(webauthn): support SimpleWebAuthn v13.3+ and v14

Upgrade the WebAuthn/Passkey providers from SimpleWebAuthn v9 to v13.3+/v14
(GHSA-6hxq-p678-4hr2, GHSA-2g3p-m8c9-hhwh). Stored credential IDs keep their
standard-base64 format and are converted to base64url only when talking to
SimpleWebAuthn, so existing Authenticator records need no migration.
next-auth and @auth/sveltekit peer ranges and client helpers are updated too.

Co-authored-by: Rastislav <rastislav@onion.email>

🎫 Affected issues

Supersedes #13378 (its commits are included). Happy to close this in favour of that PR if the author prefers to pick up these changes there.

📌 Resources

🤖 Generated with Claude Code

rastislavcore and others added 6 commits September 24, 2026 21:18
Updates `@simplewebauthn/browser` and `@simplewebauthn/server` dependencies to v13.2.2.

This upgrade brings improved security and compatibility by aligning with the latest version of the library, addressing potential vulnerabilities and ensuring better integration with modern browser environments. It also changes the way options are passed to `startAuthentication` and `startRegistration` methods.

The userID for registration options is now expected to be a Uint8Array. The authenticator's credential ID is no longer base64 encoded.
Updates the WebAuthn verification process to align with changes in the underlying SimpleWebAuthn library.

This change streamlines the extraction and usage of credential information, resulting in a cleaner and more maintainable codebase. It simplifies the verification logic by directly passing credential data.
Updates the WebAuthn and Passkey provider documentation to reflect the SimpleWebAuthn v13 API changes.

This includes clarifying the required peer dependencies, detailing the differences in the API (credential IDs as base64 strings, `verifyAuthenticationResponse` requiring a `credential` object, and the options shape for browser helpers), and updating the setup instructions accordingly.

This ensures developers using these providers with SimpleWebAuthn v13 have accurate and up-to-date information.
Updates the `@simplewebauthn/browser` and `@simplewebauthn/server` peer dependencies to version 13.2.2 in the WebAuthn and Passkey documentation.

Clarifies the usage of `@simplewebauthn/browser` dependency, emphasizing that it's only required for custom sign-in pages. Also, mentions that types are now exported from browser and server packages.
…impleWebAuthn v13/v14 upgrade

Builds on the previous commits (SimpleWebAuthn v9 -> v13 API migration) and
fixes what they left incomplete:

- Credential ID encoding. Auth.js stores credential IDs as standard base64
  (`Buffer#toString("base64")`, including "+", "/" and "=") and normalises the
  browser's ID to that format before `adapter.getAuthenticator()`. SimpleWebAuthn
  v13+ exchanges IDs as base64url. Storing `credential.id` verbatim put new rows
  in a different format from the lookup, so a newly registered passkey could
  never sign in ("WebAuthn authenticator not found in database"). IDs now stay
  standard base64 in the database, and are converted to base64url only at the
  SimpleWebAuthn boundary (allowCredentials, excludeCredentials,
  verifyAuthenticationResponse). Existing Authenticator rows need no migration.
- next-auth and @auth/sveltekit: the client helpers now call
  startAuthentication/startRegistration with `{ optionsJSON }` (v11+ API; the
  positional form only works through a deprecation shim that logs a warning).
- Peer ranges in @auth/core, next-auth and @auth/sveltekit:
  `@simplewebauthn/server` ^13.3.2 || ^14.0.2 and `@simplewebauthn/browser`
  ^13.3.0 || ^14.0.0. The server floors carry GHSA-6hxq-p678-4hr2 (fixed in
  13.3.2) and GHSA-2g3p-m8c9-hhwh (fixed in 14.0.2; v14 requires Node 22+, so
  v13.3 stays allowed for Node 20). next-auth and @auth/sveltekit still
  declared ^9, which made `npm install` fail with ERESOLVE next to v13.
- Default sign-in page browser script: v13.3.0. devDependencies: v14. The
  nextjs example: @simplewebauthn/server ^14.0.2.
- Tests: fixture credential IDs now contain "+", "/" and "=" (hex IDs were
  identical in base64 and base64url, which is why the encoding bug passed), the
  browser response carries base64url as real browsers do, and two tests cover
  the conversion helpers.

Co-authored-by: Rastislav <rastislav@onion.email>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ged credential storage

Install instructions now point at the patched releases, note the Node 22 requirement of v14 (v13.3 for Node 20), and state that existing Authenticator records keep working without a data migration.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 24, 2026

Copy link
Copy Markdown

@rvdlaar is attempting to deploy a commit to the authjs Team on Vercel.

A member of the Team first needs to authorize it.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​simplewebauthn/​browser@​9.0.1 ⏵ 14.0.0100 +110092 +688 +6100
Updated@​simplewebauthn/​server@​9.0.3 ⏵ 14.0.298100 +188 +192 +1100

View full report

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants