Prerequisites
Before you start, make sure you have:- A Prelude account with access to Prelude Auth
- An Application ID (
appID) — see Applications - Your Management API key for backend calls
- A frontend served over HTTPS, or
http://localhost:<port>for local development — the WebAuthn API refuses any other origin
Set up passkey authentication
Configure the WebAuthn Relying Party identity for your app. The RP identity is shared across every passkey ceremony — changing it after credentials are registered invalidates them at the authenticator layer, so set it once per environment.1
Create a passkey configuration
2
Declare verify_passkey on the step-up configuration
Add the step key to your step-up configuration and reference it from any scope whose challenge should require a passkey.A registered passkey shows up on the user as an identifier of type
passkey, so direct-mode entries select on it via identifier_types like any other identifier.To express a passkey-or-OTP fallback, list two direct entries on the same scope — the passkey-gated one first, the OTP fallback second. The runtime serves the first one whose identifier types the user holds:Enable passwordless login (optional)
Setlogin_enabled: true on the PasskeyConfig to opt the app into primary-factor passkey sign-in. While the flag is on, registration also requests a discoverable credential (residentKey: required) so the resulting passkey shows up in the browser’s autofill chip.
passkey_registration_failed after the flag flips on.
Enterprise authenticator policy (optional)
Restrict registration to specific authenticator models via the AAGUID allowlist / blocklist on the PasskeyConfig. Pairs withattestation_preference: "direct" or "enterprise" — with "none" most authenticators return an all-zeros AAGUID and the allowlist matches nothing.
Subscribe to passkey lifecycle events (optional)
Three webhook events surface passkey activity for audit and user notifications:
Subscribe via the existing webhook configuration:
Surface passkey state in access tokens (optional)
The custom-claims pipeline exposes ahas_passkey template input. Map it on your app’s claims configuration to let your frontend decide whether to prompt the user to enrol: