Skip to content

feat(webauthn): support MFA-session passkey registration#47

Merged
lakhansamani merged 3 commits into
mainfrom
feat/mfa-session-webauthn-registration
Jul 20, 2026
Merged

feat(webauthn): support MFA-session passkey registration#47
lakhansamani merged 3 commits into
mainfrom
feat/mfa-session-webauthn-registration

Conversation

@lakhansamani

Copy link
Copy Markdown
Contributor

Summary

  • webauthn_registration_options/webauthn_registration_verify can now authenticate via the MFA session cookie (email/phone_number/state), not just a bearer token — needed so a passkey can be registered during a token-withheld MFA offer, before any token exists.
  • webauthnRegistrationVerify/registerPasskey now return the full AuthToken shape (access_token populated when this call completed the withheld MFA gate) instead of a bare message.
  • parseMfaRedirectParams gains mfaGate: 'offer' | 'verify' so a host app can tell a first-time enrollment offer from a challenge for an already-configured factor.

Backend counterpart: authorizerdev/authorizer#<branch feat/enforce-mfa-passkey-gate>.

Test plan

  • npx jest __test__/webauthnMethods.test.ts __test__/mfaRedirect.test.ts — all passing, new cases added
  • npm run build — clean
  • npx tsc --noEmit (consumer side, via authorizer-react) — clean

webauthn_registration_options/verify required a bearer token,
unavailable during a token-withheld MFA offer. Add email/phone_number/
state so registerPasskey can authenticate via the MFA session cookie
instead, and return the full AuthResponse (access_token on success)
rather than a bare message.

Also add mfa_gate to parseMfaRedirectParams so callers can tell an
enrollment offer from a challenge for an already-configured factor.
@lakhansamani
lakhansamani merged commit 1a027ac into main Jul 20, 2026
0 of 2 checks passed
@lakhansamani
lakhansamani deleted the feat/mfa-session-webauthn-registration branch July 20, 2026 05:51
lakhansamani added a commit to authorizerdev/authorizer-react that referenced this pull request Jul 20, 2026
)

* feat(mfa): offer passkey enrollment during the login-time MFA setup

AuthorizerMFASetup hardcoded passkey: false everywhere it was rendered
with loginContext set (login/signup/OAuth/passkey-primary), and
force-hid the option whenever loginContext was present at all -
because webauthn_registration_options/verify required a bearer token
that doesn't exist until the MFA gate is satisfied. The backend
(authorizerdev/authorizer, authorizerdev/authorizer-js#47) now
supports authenticating that ceremony via the MFA session cookie
instead, so passkey is a real option here.

Also, using the new mfaGate signal on the OAuth/magic-link return
path: route to AuthorizerVerifyOtp for an already-configured factor
instead of always showing the setup screen, and let
AuthorizerPasskeyLogin's verify step render the same challenge form
(previously told the user to sign in with a password instead).

Removed a stale useEffect in AuthorizerVerifyOtp that flagged "Email
or Phone Number is required" on mount even for the legitimate
session-only resolution path these two flows now exercise.

* feat(mfa): passkey login + MFA setup components, shared BackLink

* chore(release): bump authorizer-js to 3.3.0-rc.2; v2.2.0-rc.1
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.

1 participant