Skip to content

feat: add passkey login option to the login screen#54

Merged
lakhansamani merged 4 commits into
mainfrom
feat/webauthn-passkey-login
Jul 11, 2026
Merged

feat: add passkey login option to the login screen#54
lakhansamani merged 4 commits into
mainfrom
feat/webauthn-passkey-login

Conversation

@lakhansamani

Copy link
Copy Markdown
Contributor

Summary

New AuthorizerPasskeyLogin component offers a full passwordless, usernameless "Sign in with a passkey" button (discoverable-credential login) above the existing login methods, wired via authorizer-js's loginWithPasskey().

  • Renders nothing when isWebauthnSupported() is false — purely a browser capability check, no server config flag (unlike social login)
  • Composed into AuthorizerRoot on the login view only

Scope decision

Passkey management (list/add/delete existing passkeys) is intentionally out of scope here — this library has only ever contained login-flow components, there's no account/settings page to extend, and the raw SDK methods (webauthnCredentials, webauthnDeleteCredential, registerPasskey) are already exported for a consuming app (web/app, or any other) to build that itself.

Dependency (blocking merge)

Depends on authorizerdev/authorizer-js#40 (isWebauthnSupported, loginWithPasskey), which itself depends on authorizerdev/authorizer-js#39 — neither published to npm yet. package.json here is intentionally untouched, verified locally via npm link.

Test plan

  • tsc --noEmit clean, build clean (ESM/CJS)
  • Live-verified in the example/ app against a real local backend (branch feat/webauthn-passkey-backend, authorizerdev/authorizer#671): button renders correctly, click fires webauthn_login_options and invokes the browser's native navigator.credentials.get() (confirmed via loading state, no console errors)
  • Completing a full ceremony live needs real biometric hardware or a manually-configured Chrome DevTools virtual authenticator — neither is scriptable through browser automation. The ceremony logic itself is proven correct by a Go integration test (authorizerdev/authorizer#671) that simulates a real authenticator via github.com/descope/virtualwebauthn through the actual server code path.

New AuthorizerPasskeyLogin component offers a full passwordless,
usernameless "Sign in with a passkey" button (discoverable-credential
login) above the existing login methods, wired via authorizer-js's
loginWithPasskey() (authorizerdev/authorizer-js#40 / backend:
authorizerdev/authorizer#671).

Renders nothing when isWebauthnSupported() is false - there's no
server config flag for passkeys (unlike social login), it's purely a
browser capability check.

Passkey management (list/add/delete existing passkeys) is out of
scope here: this library only ever contained login-flow components,
no account/settings page exists to extend, and the raw SDK methods
(webauthnCredentials, webauthnDeleteCredential, registerPasskey) are
already exported for a consuming app to build that itself.

Live-verified in the example app against a real backend: the button
renders, click fires webauthn_login_options over GraphQL and invokes
the browser's native navigator.credentials.get() correctly (confirmed
via the loading state and no console errors). Completing an actual
ceremony needs real biometric hardware or a manually-configured
Chrome DevTools virtual authenticator, neither of which is scriptable
through browser automation - the ceremony logic itself is already
proven correct by a Go integration test that simulates a real
authenticator via github.com/descope/virtualwebauthn through the
actual server code path.
The "OR" separator after the passkey button rendered unconditionally
whenever WebAuthn was supported, regardless of whether basic auth,
magic link, or any social login was actually enabled. If none were,
the login view showed the passkey button followed by a dangling "OR"
with nothing beneath it. Gated on the same conditions AuthorizerRoot
uses to decide whether AuthorizerSocialLogin, AuthorizerBasicAuthLogin,
or AuthorizerMagicLinkLogin render anything.
@lakhansamani

Copy link
Copy Markdown
Contributor Author

Fixed the dangling separator finding: the "OR" now only renders when another login method (social, basic auth, or magic link) is actually going to show below it, mirroring AuthorizerRoot's own visibility conditions.

@lakhansamani

Copy link
Copy Markdown
Contributor Author

Blocked on an authorizer-js release. This calls isWebauthnSupported() / loginWithPasskey(), which ship in authorizer-js (WebAuthn methods merged, not yet published). CI fails on published 3.2.1. Unblock: publish authorizer-js, bump the dep here, then CI will go green (the component itself type-checks against the dev SDK).

@lakhansamani
lakhansamani merged commit 34143b3 into main Jul 11, 2026
4 of 5 checks 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