Implementing Passkey Authentication
Passkeys let your users sign in without a password, using their device’s biometrics (Touch ID, Windows Hello) or a hardware security key. Skycloak ships this as a built-in Recipe — a pre-built, realm-scoped configuration you turn on from the dashboard. There’s no custom authenticator to write and no separate frontend/backend to build; this tutorial walks through enabling it and understanding what it changes under the hood.
What Are Passkeys?
Passkeys are the WebAuthn/FIDO2 standard applied to everyday login:
- A public/private key pair is generated on the user’s device; only the public key is ever sent to Keycloak
- Signing in proves possession of the private key (via biometrics or a PIN-protected security key) — there’s no shared secret that can be phished, leaked, or reused across sites
- Faster than typing a password, and nothing for the user to remember or rotate
Prerequisites
- A running Keycloak cluster with at least one realm
- Workspace Owner or Admin role
- Users’ browsers/devices should support WebAuthn (all current major browsers and OS platform authenticators do)
How the Recipe Works
The Passkeys recipe is realm-scoped — you apply it once per realm, and it’s tracked independently per cluster/realm. Turning it on does three things to that realm:
-
Enables the
webauthn-register-passwordlessrequired action, so users can register a passkey. -
Builds a separate
browser-passkeyauthentication flow — identity-first (username first, then passkey), with password and conditional OTP kept as a fallback for anyone without a registered passkey yet. Your existingbrowserflow is left untouched. - If you check “Enforce for all users”, retro-applies the required action to every existing user in the realm, so they’re prompted to register a passkey on their next login.
After applying, Skycloak also best-effort re-applies your realm’s login theme, since the new identity-first flow renders a couple of pages a previously generated custom theme might not include yet. This step is non-blocking — if it fails, the recipe still completes and you can re-apply branding manually from the Branding page.
Step 1: Open Recipes
- Select your cluster and realm in the sidebar’s context stack
- Click “Recipes” in the sidebar — this opens the realm’s Configuration Recipes page, a grid of pre-built configuration cards
Step 2: Start the Passkeys Recipe
- Find the “Passkeys” card (category: Authentication, difficulty: Easy, about 1 minute)
- Click “Start Recipe”
Step 3: Configure and Enable
- You’ll land on a single-step wizard headed “Enable passkeys”
- Optional: check “Enforce for all users” — if checked, every existing user in the realm gets prompted for passkey registration on their next login. Leave it unchecked if you’d rather let users opt in gradually.
- Click “Enable” (or “Enable & enforce” if you checked the box)
The recipe applies immediately — no separate “apply” step or propagation delay to wait on.
Step 4: Test the Flow
- Open an incognito window and go to your realm’s login page (or a connected application)
- New user / first login after enabling: after entering a username, the user is prompted to register a passkey (or complete password + OTP if their device doesn’t support WebAuthn)
- Returning user with a registered passkey: enters their username, then completes sign-in with Touch ID, Windows Hello, or a security key — no password step at all
-
Existing user under “Enforce”: on next login, they’re walked through registering a passkey via the
webauthn-register-passwordlessrequired action before they can continue
Passkeys vs. the MFA (WebAuthn) Recipe
Don’t confuse this with the separate MFA recipe (mfa-webauthn) — they solve different problems and compose well together:
| Passkeys recipe | MFA (WebAuthn) recipe | |
|---|---|---|
| Role of WebAuthn | Primary factor, replaces the password | Second factor, on top of the password |
| Flow changes | Builds a new browser-passkey identity-first flow |
Adds to the stock browser flow, no rebuild |
| Login experience | Username, then passkey (password is a fallback) | Username + password, then WebAuthn/TOTP challenge |
Enabling both lets users who’ve registered a passkey sign in passkey-first, while users without one still get username/password plus a second factor as a fallback path.
Troubleshooting
Passkey prompt doesn’t appear:
- Confirm the
webauthn-register-passwordlessrequired action shows as enabled for the realm - Check the user’s browser and device support WebAuthn (most modern browsers and OS platform authenticators do; older browsers may not)
Login page looks off after enabling:
- The post-enable theme reapply is best-effort — if your realm uses a custom theme, revisit the Branding page and re-apply it manually if anything looks inconsistent
Users without a passkey-capable device get stuck:
- The identity-first flow keeps password + conditional OTP as a fallback by default, so nobody is locked out — if a user reports otherwise, check whether “Enforce” was applied and whether their account has a password credential set
Best Practices
- Roll out gradually: apply without “Enforce” first, let early adopters register passkeys, then enforce once you’re confident in the flow
- Communicate before enforcing: users forced into a required action mid-login can be caught off guard — a heads-up email helps
- Test across devices: verify the flow on at least one mobile and one desktop browser before enforcing realm-wide