Skip to content

fix(passkey): an account with a passkey starts by proving the email - #206

Merged
Mikefluff merged 1 commit into
mainfrom
fix/passkey-registration-proves-email
Sep 27, 2026
Merged

Mikefluff merged 1 commit into
mainfrom
fix/passkey-registration-proves-email

Conversation

@Mikefluff

Copy link
Copy Markdown
Member

POST /auth/passkey/prepare-registration was unauthenticated and created an
account for any unclaimed email with emailVerified: true, started a session and
returned a token, before the address was proven or a passkey existed. Anyone
could claim somebody else's address; any RP trusting email_verified (course
purchases on mikefluff.com are looked up by it) treated them as its owner, and
the real owner signing in later by magic link landed in an account holding the
claimant's passkey.

  • The endpoint and AuthService.createUserForPasskey are gone. Registration is
    the email one-time code (/auth/otp/request, /auth/otp/verify: proves the
    address, creates the account, starts the session), then the authenticated
    /passkey/registration/options + verify. An existing account just gets a
    passkey added, so the 'already exists' loop is gone too.
  • Passkey sign-in goes through establishSession (regenerate, signed cookie)
    instead of mutating the session it arrived with. amr carries mfa only when
    the authenticator verified the user.
  • authentication/options ignores email: scoping by it returned that
    address's credential ids. Sign-in is discoverable-only; registration
    requires a resident key, drops the platform-only restriction and the
    client-device hint so security keys and phones work, and throttles options.
  • The sign-in page no longer flips to registration on 'passkey not found'.

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FdVXRzoHDTGciZtVhbhy8y

POST /auth/passkey/prepare-registration was unauthenticated and created an
account for any unclaimed email with emailVerified: true, started a session and
returned a token, before the address was proven or a passkey existed. Anyone
could claim somebody else's address; any RP trusting email_verified (course
purchases on mikefluff.com are looked up by it) treated them as its owner, and
the real owner signing in later by magic link landed in an account holding the
claimant's passkey.

- The endpoint and AuthService.createUserForPasskey are gone. Registration is
  the email one-time code (/auth/otp/request, /auth/otp/verify: proves the
  address, creates the account, starts the session), then the authenticated
  /passkey/registration/options + verify. An existing account just gets a
  passkey added, so the 'already exists' loop is gone too.
- Passkey sign-in goes through establishSession (regenerate, signed cookie)
  instead of mutating the session it arrived with. amr carries mfa only when
  the authenticator verified the user.
- authentication/options ignores email: scoping by it returned that
  address's credential ids. Sign-in is discoverable-only; registration
  requires a resident key, drops the platform-only restriction and the
  client-device hint so security keys and phones work, and throttles options.
- The sign-in page no longer flips to registration on 'passkey not found'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdVXRzoHDTGciZtVhbhy8y
@Mikefluff
Mikefluff merged commit b1d79a3 into main Sep 27, 2026
10 checks passed
@Mikefluff
Mikefluff deleted the fix/passkey-registration-proves-email branch September 27, 2026 20:23
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