Skip to content

fix(auth0-auth-js): surface mfa_required from passkey token exchange#187

Merged
nandan-bhat merged 2 commits into
mainfrom
fix/SDK-9625
Jun 12, 2026
Merged

fix(auth0-auth-js): surface mfa_required from passkey token exchange#187
nandan-bhat merged 2 commits into
mainfrom
fix/SDK-9625

Conversation

@nandan-bhat

@nandan-bhat nandan-bhat commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

When MFA is enabled, Auth0's passkey token exchange (getTokenByPasskey, grant urn:okta:params:oauth:grant-type:webauthn) returns an mfa_required error at /oauth/token with an mfa_token — just like ROPG and other grants. The SDK was dropping mfa_token / mfa_requirements when building PasskeyGetTokenError, so isMfaRequiredError() returned false and callers couldn't continue into the MFA flow after a passkey login. Passkey was the only token method missing this parity.

Changes

  • passkey-client.tsgetTokenByPasskey now normalizes the thrown error via the shared toOAuth2Error, lifting mfa_token / mfa_requirements from the nested openid-client error cause.
  • errors.ts — moved toOAuth2Error out of auth-client.ts into errors.ts (a leaf module) and exported it, so auth-client and passkey-client share one canonical implementation with no circular import. isMfaRequiredError JSDoc now lists getTokenByPasskey.
  • auth-client.ts — imports toOAuth2Error from errors.js; behavior unchanged for existing flows.
  • passkey/errors.ts — added PasskeyGetTokenApiErrorResponse and scoped the MFA fields to PasskeyGetTokenError only. PasskeyRegisterError / PasskeyChallengeError keep the base type, since challenge requests can't return mfa_required.
  • EXAMPLES.md — documented MFA handling for getTokenByPasskey.

Usage

try {
  await authClient.passkey.getTokenByPasskey({ authSession, credential });
} catch (error) {
  if (isMfaRequiredError(error)) {
    await authClient.mfa.challengeAuthenticator({
      mfaToken: error.cause.mfa_token,
      challengeType: 'otp',
    });
  }
}

Breaking changes

None. Additive only — error class names, code values, and constructor signatures are unchanged. Verified backward-compatible with auth0-spa-js.

Note for reviewers

toOAuth2Error is now reachable from the public type surface (via export * in index.ts), alongside the pre-existing transitive leak of GrantRequestFn / *ClientOptions. This is intentionally deferred to a follow-up PR that will tighten the public API (e.g. stripInternal) in one deliberate, verified change — out of scope here.

Testing

New tests: mfa_token / mfa_requirements preserved on PasskeyGetTokenError.cause; isMfaRequiredError() recognizes it.

Summary by CodeRabbit

  • Bug Fixes

    • Improved MFA error handling for passkey sign-in so MFA details (MFA token and requirements) are preserved and surfaced for recovery flows.
  • Documentation

    • Expanded guidance and examples for handling MFA-required responses during passkey authentication, including how to continue with an MFA challenge.
  • Tests

    • Added tests covering MFA error scenarios in the passkey token exchange.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5213718f-70e1-47d8-b692-1bd2e5f06238

📥 Commits

Reviewing files that changed from the base of the PR and between d768d0b and efb8caf.

📒 Files selected for processing (6)
  • packages/auth0-auth-js/EXAMPLES.md
  • packages/auth0-auth-js/src/auth-client.ts
  • packages/auth0-auth-js/src/errors.ts
  • packages/auth0-auth-js/src/passkey/errors.ts
  • packages/auth0-auth-js/src/passkey/passkey-client.spec.ts
  • packages/auth0-auth-js/src/passkey/passkey-client.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/auth0-auth-js/src/passkey/passkey-client.ts
  • packages/auth0-auth-js/src/auth-client.ts
  • packages/auth0-auth-js/src/errors.ts
  • packages/auth0-auth-js/EXAMPLES.md
  • packages/auth0-auth-js/src/passkey/errors.ts

📝 Walkthrough

Walkthrough

This PR enables MFA error handling for passkey token exchange by introducing a shared error normalizer that lifts mfa_token and mfa_requirements from nested response payloads to top-level OAuth2Error fields, updates passkey error types to preserve MFA context, integrates the normalizer into the token exchange flow, and documents the behavior with tests and examples.

Changes

Passkey MFA Error Handling Support

Layer / File(s) Summary
Error Normalization Foundation
src/errors.ts, src/auth-client.ts
New toOAuth2Error(e: unknown): OAuth2Error function normalizes unknown errors and lifts mfa_token/mfa_requirements from cause to top-level fields. Auth-client refactored to import and delegate to this shared normalizer instead of maintaining a local implementation.
Passkey Error Types for MFA
src/passkey/errors.ts
PasskeyGetTokenApiErrorResponse interface extends base error response with optional mfa_token and mfa_requirements fields. PasskeyGetTokenError updated to accept and preserve this specialized response shape in its cause property.
Passkey Token Exchange Error Handling
src/passkey/passkey-client.ts
getTokenByPasskey now uses toOAuth2Error() to normalize caught errors before creating PasskeyGetTokenError, ensuring MFA context is preserved in the error thrown to callers.
Tests and Documentation
src/passkey/passkey-client.spec.ts, EXAMPLES.md
New test cases validate that getTokenByPasskey throws PasskeyGetTokenError with cause.mfa_token and cause.mfa_requirements preserved when the backend returns mfa_required. Documentation updated to include passkey.getTokenByPasskey in the list of token methods supporting MFA-required responses and includes code example using isMfaRequiredError type guard.

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested Reviewers

  • pmathew92

🐰 A new path for MFA dreams,
Passkeys now flowing through error streams,
MFA tokens lifted bright,
Type-safe and tight,
Your auth code glistens with moonbeams!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically summarizes the main objective of the changeset: surfacing MFA-required errors from passkey token exchange in the auth0-auth-js SDK.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/SDK-9625

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nandan-bhat nandan-bhat merged commit 835c400 into main Jun 12, 2026
16 checks passed
@nandan-bhat nandan-bhat deleted the fix/SDK-9625 branch June 12, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants