Skip to content

feat(auth): sign in with just a passkey, no email or password - #18

Merged
CubityFirst merged 1 commit into
mainfrom
feat/passkey-sign-in
Nov 12, 2026
Merged

CubityFirst merged 1 commit into
mainfrom
feat/passkey-sign-in

Conversation

@CubityFirst

Copy link
Copy Markdown
Owner

Passkeys only worked as a second step after typing an email and password. Now you can sign in with a passkey alone: hit Sign in with a passkey on the login page, pick one, and you're in.

The new /webauthn/auth/passkey/{start,finish} ceremony sends an empty allowCredentials, so the browser offers any passkey it holds for the site. The server resolves the account from the chosen credential, requires the userHandle to match it, then runs the same moderation, email-verification and forced-password-change gates as password login (via the now-shared issueSessionOrForceChange). Those gates run only after the assertion verifies, so a bare credential id can't be used to probe an account's state.

  • No migration: the user-less challenge row uses a sentinel user_id under its own passkey ceremony type, so it can't be swapped with a user-bound challenge.
  • Same per-IP RATE_LIMITER_AUTH as the existing ceremony; the API worker already proxies the /webauthn/auth prefix.
  • Only the auth and frontend workers change.
  • Older hardware keys that can't store a resident credential won't show up for the button; they still work via email → password → key.

Tests: new unit tests for the discoverable assertion check (wrong/missing userHandle refused, challenge scoped to the passkey type) and the finish route; auth (412) and frontend (917) suites pass. Added an e2e case to 2fa.spec.ts using the virtual authenticator; not run locally yet.

Add a discoverable-credential WebAuthn ceremony at
/webauthn/auth/passkey/{start,finish}. Start sends an empty
allowCredentials so the browser offers any passkey for the RP; finish
resolves the account from the chosen credential, requires the userHandle
to match it, then applies the same moderation, email-verification and
forced-password-change gates as password login via the now-shared
issueSessionOrForceChange.

The login page gets a "Sign in with a passkey" button, and the Settings
copy now says passkeys can replace the password. No migration: the
user-less challenge uses a sentinel user_id under its own ceremony type.
@CubityFirst
CubityFirst merged commit a10b002 into main Nov 12, 2026
1 check passed
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