Implementing Passkeys in Keycloak: FIDO2 & WebAuthn Setup Guide
Step-by-step guide to implementing passwordless authentication with passkeys in Keycloak 26. Covers FIDO2/WebAuthn configuration, progressive enrollment, fallback flows, and real-world deployment tips.
KeycloakPro Team
KeycloakPro Team
Passwords Are Dead. Passkeys Won.
In 2026, the shift is complete. Apple, Google, and Microsoft now default to passkeys for new account creation. The FIDO Alliance reports over 15 billion accounts use passkeys worldwide. If your identity system still relies solely on passwords, you are shipping a legacy experience with known security holes.
Keycloak has supported WebAuthn since version 15, and Keycloak 26 ships with mature, production-ready passkey support out of the box. This guide walks you through the full implementation: from enabling FIDO2 in the admin console to building progressive enrollment flows that migrate your existing user base to passwordless authentication.
What Are Passkeys (and How Do They Work)?
A passkey is a FIDO2/WebAuthn credential that replaces passwords with public-key cryptography. Instead of a shared secret (a password stored on both client and server), passkeys use a key pair:
- Registration -- The user's device generates a public/private key pair. The public key is sent to the server. The private key never leaves the device.
- Authentication -- The server sends a challenge. The device signs it with the private key (after biometric or PIN verification). The server verifies the signature with the stored public key.
There is no password to phish, leak, or brute-force. The private key is bound to the origin (your domain), so even a perfect phishing site on a different domain cannot intercept it.
Platform vs. Roaming Authenticators
| Type | Examples | Sync Across Devices | Best For |
|---|---|---|---|
| Platform | Touch ID, Face ID, Windows Hello, Android biometrics | Yes (via iCloud Keychain, Google Password Manager) | Consumer apps, everyday login |
| Roaming | YubiKey, Titan Security Key, SoloKeys | No (bound to physical device) | Enterprise, high-security environments |
Keycloak supports both. Most deployments will use platform authenticators for convenience, with roaming authenticators as a backup or for elevated-privilege operations.
Keycloak's Passkey Support
| Keycloak Version | WebAuthn Support |
|---|---|
| 15 - 21 | Basic WebAuthn 2FA (experimental) |
| 22 - 24 | Stable WebAuthn, passwordless policy option |
| 25 | Conditional UI (autofill) support, improved passkey UX |
| 26 | Full passkey support, discoverable credentials, cross-device authentication |
This guide targets Keycloak 26.x. If you are running an older version, upgrade first -- the passkey experience in earlier versions is significantly more limited.
Step-by-Step Configuration
1. Enable the WebAuthn Passwordless Policy
Navigate to Authentication > Policies > WebAuthn Passwordless Policy in the Keycloak admin console and configure the following:
Relying Party Entity Name: YourApp
Signature Algorithms: ES256, RS256
Relying Party ID: yourdomain.com
Attestation Conveyance: none
Authenticator Attachment: <not set> (allows both platform and roaming)
Require Discoverable Credential: Yes
User Verification Requirement: required
Timeout: 60 (seconds)
Key settings explained:
- Relying Party ID must match your domain exactly. Subdomains are allowed (e.g.,
auth.yourdomain.com), but the RP ID must be a registrable domain suffix. - Require Discoverable Credential: Yes is essential for true passkey behavior. Without it, the server must supply a credential ID, which defeats the purpose.
- User Verification: required ensures the user must prove presence via biometric or PIN. Never set this to
discouragedin production.
2. Create a Passwordless Authentication Flow
Go to Authentication > Flows and duplicate the built-in Browser flow. Name it Browser with Passkeys. Configure it as follows:
Browser with Passkeys
|-- Cookie [ALTERNATIVE]
|-- Passkey or Password [ALTERNATIVE] (subflow)
| |-- WebAuthn Passwordless [ALTERNATIVE]
| |-- Username Password Form [ALTERNATIVE]
| |-- Passkey Enrollment Prompt [CONDITIONAL]
| |-- Condition - User Configured
| Negated: true
| |-- WebAuthn Passwordless Register [REQUIRED]
This flow does the following:
- If a valid session cookie exists, skip authentication entirely.
- Try passkey authentication first (the browser shows the passkey autofill prompt).
- If the user has no passkey registered, fall back to username and password.
- After password login, prompt users who lack a passkey to enroll one (progressive enrollment).
3. Bind the Flow to Your Realm
Go to Authentication > Bindings and set:
- Browser Flow:
Browser with Passkeys
Save the binding. All browser-based logins for this realm now use the passkey-first flow.
4. Register a Passkey (User Perspective)
Once the flow is active, users see this experience:
- New users -- After initial registration (or first password login), a prompt appears: "Secure your account with a passkey." The browser triggers the WebAuthn ceremony, and the user confirms with Touch ID, Face ID, or a security key.
- Returning users -- The login page shows a passkey autofill suggestion. One tap and a biometric check, and they are in.
5. Configure Client-Side Conditional UI (Optional)
For the best passkey UX, enable conditional mediation on your login page. If you are using Keycloak's default theme, this is handled automatically in Keycloak 26. For custom themes, add the autocomplete="webauthn" attribute to your username field:
<input
type="text"
name="username"
autocomplete="username webauthn"
placeholder="Email or username"
/>
This tells the browser to offer stored passkeys in the autofill dropdown, which is the modern passkey experience users expect.
Progressive Enrollment Strategy
You cannot flip a switch and force all users to passkeys overnight. A progressive enrollment strategy handles the transition gracefully:
Phase 1 -- Offer (Weeks 1-4) Prompt users to create a passkey after each password login. Make it skippable. Track enrollment rates.
Phase 2 -- Nudge (Weeks 5-8) Increase prompt frequency. Show a banner: "Passkeys are faster and more secure." Require passkey enrollment for new accounts.
Phase 3 -- Require (Weeks 9+)
Make passkey enrollment mandatory for all users. Allow password as a fallback only for devices that do not support WebAuthn (check with PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()).
To track who has enrolled, query the Keycloak user credentials API:
curl -s -H "Authorization: Bearer $TOKEN" \
"https://auth.yourdomain.com/admin/realms/myrealm/users/$USER_ID/credentials" \
| jq '.[] | select(.type == "webauthn-passwordless")'
Fallback Flows for Unsupported Devices
Not every device supports passkeys. Older browsers, corporate-locked machines, and some embedded webviews lack WebAuthn support. Your authentication flow must handle this gracefully.
The flow structure in Step 2 already handles this -- WebAuthn Passwordless is set to ALTERNATIVE, so if the browser does not support WebAuthn, Keycloak falls through to the password form automatically.
For additional robustness, detect support client-side:
async function checkPasskeySupport() {
if (!window.PublicKeyCredential) {
return { supported: false, reason: "WebAuthn API not available" };
}
const platformAvailable =
await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
return {
supported: true,
platformAuthenticator: platformAvailable,
};
}
Use this to conditionally show passkey-related UI elements only when the browser supports them.
Security Benefits
Passkeys eliminate entire categories of attacks:
| Attack Vector | Password Vulnerable | Passkey Vulnerable |
|---|---|---|
| Phishing | Yes | No (origin-bound) |
| Credential stuffing | Yes | No (no shared secret) |
| Brute force | Yes | No (public-key crypto) |
| Database breach | Yes (even hashed) | No (server stores only public key) |
| Man-in-the-middle | Depends on implementation | No (challenge-response with TLS binding) |
| SIM swapping (SMS OTP) | Yes | No (device-bound) |
The server never stores anything secret. Even a complete database compromise yields only public keys, which are useless to an attacker.
Common Pitfalls
1. Relying Party ID Mismatch
The RP ID must match the domain where the login page is served. If your Keycloak is at auth.example.com but you set RP ID to example.com, passkeys will work. But if you set RP ID to auth.example.com, passkeys registered there will not work on app.example.com. Plan your RP ID to be the broadest usable domain.
2. Missing Discoverable Credential Requirement
If you do not set Require Discoverable Credential: Yes, Keycloak creates server-side credentials that require the username first. This breaks the "just tap to sign in" experience.
3. Cross-Device Authentication Gaps
Users who register a passkey on their phone may expect it to work on their laptop. This works with synced passkeys (iCloud Keychain, Google Password Manager) but not with roaming authenticators. Encourage users to register passkeys on each device, or ensure they use a syncing platform.
4. Corporate Environments Blocking WebAuthn
Some enterprise group policies disable the WebAuthn API. Test in your target environment. If WebAuthn is blocked, your fallback flow is essential.
5. User Education
Users who have never seen a passkey prompt may be confused. Customize the Keycloak theme to include clear instructions: "Use your fingerprint, face, or security key to sign in." A brief animation or illustration reduces support tickets significantly.
Testing and Validation
Before going to production, validate every path:
Functional Testing Checklist
- Register a platform passkey (Touch ID / Windows Hello)
- Register a roaming passkey (YubiKey)
- Log in with a registered passkey
- Log in with password when no passkey is registered
- Progressive enrollment prompt appears after password login
- Fallback works when WebAuthn is unavailable (e.g., incognito with security key unplugged)
- Cross-device login works (QR code flow from desktop to phone)
- Multiple passkeys per user (register two, log in with either)
- Delete a passkey from account management, verify it no longer works
Browser Compatibility Matrix
| Browser | Platform Passkey | Roaming Passkey | Conditional UI |
|---|---|---|---|
| Chrome 126+ | Yes | Yes | Yes |
| Safari 18+ | Yes | Yes | Yes |
| Firefox 125+ | Yes | Yes | Yes |
| Edge 126+ | Yes | Yes | Yes |
| Samsung Internet 24+ | Yes | Yes | Partial |
Run automated tests using the WebAuthn testing tools or Keycloak's built-in test utilities. For CI environments where biometric prompts cannot be answered, use virtual authenticators available in Chrome DevTools (Protocol > WebAuthn).
Wrapping Up
Passkeys are not an emerging technology anymore -- they are the standard. Keycloak 26 gives you everything you need to implement them: WebAuthn policies, flexible authentication flows, progressive enrollment, and graceful fallbacks.
The configuration itself is straightforward. The real work is in the rollout strategy: educating users, handling edge cases across device ecosystems, and building monitoring to track enrollment progress.
Need help implementing passkeys in your Keycloak deployment? The KeycloakPro team has deployed passkey-first authentication for organizations ranging from startups to enterprises with millions of users. We handle the flow design, theme customization, cross-device testing, and progressive rollout strategy so your team can focus on your product. Get in touch for a free architecture review.
Need Help With Keycloak?
Our team specializes in production-grade Keycloak deployments. Get a free 30-minute strategy consultation.
Book a Free Strategy Call