Conditional Mediation Browser Support Matrix
Passkey autofill either appears in the username dropdown or it does not — and when it does not, the failure is silent, with no error thrown. The single most reliable cause is that PublicKeyCredential.isConditionalMediationAvailable() returned false on the user’s engine, or the annotation and credential preconditions were not met. This page gives the current support picture, explains why autofill silently no-ops, and shows how to gate on runtime feature detection rather than a hardcoded table. It sits under Conditional Mediation and Passkey Autofill UI.
Support Snapshot and Preconditions
Autofill requires all three of the following to be true simultaneously. Scan-match your situation against the table before assuming a browser bug.
| Precondition | Signal | Failure symptom |
|---|---|---|
| Engine supports conditional mediation | isConditionalMediationAvailable() → true |
Dropdown shows saved usernames only, no passkeys |
| Input is annotated | autocomplete="username webauthn" |
No passkey suggestion on any engine |
| Credential is discoverable | Registered with residentKey: 'required' |
Autofill empty for this account |
| Secure context | HTTPS or localhost |
get() rejects with SecurityError |
get() issued on load |
mediation: 'conditional' before focus |
Suggestions never populate |
The visual below maps the three independent capabilities to the resulting UX — most “autofill is broken” reports are actually the middle or right column.
Current expectations (verify at runtime): Safari 16+ (iOS/iPadOS/macOS), Chrome/Edge 108+ (Android/desktop), and Windows Hello via Chromium return true. Firefox support is version-dependent and has historically lagged. These are expectations, not guarantees — OS version, private browsing, and managed-device policy all shift the result.
Root Cause Analysis
1. isConditionalMediationAvailable() returns false. The engine does not implement conditional mediation, or the method itself is absent (older Chromium). Autofill will never populate; you must fall back to a modal “Sign in with a passkey” button.
2. Missing or malformed autocomplete token. The field has autocomplete="username" without the appended webauthn, or uses the standalone autocomplete="webauthn". The browser never decorates the field.
3. The account has only non-discoverable credentials. Server-side credentials cannot be enumerated by the authenticator without a credentialId hint, so autofill has nothing to offer. This is the direct consequence of the discoverable vs server-side credentials choice.
4. The conditional get() was never issued (or was issued behind a gesture). If the call runs only inside a click handler, the browser has no pending request to attach suggestions to when the user focuses the field.
Step-by-Step Resolution
Step 1 — Gate on the runtime probe, not a version table
async function autofillGate(): Promise<'autofill' | 'modal' | 'password'> {
if (typeof window.PublicKeyCredential === 'undefined') return 'password';
const canAutofill =
typeof PublicKeyCredential.isConditionalMediationAvailable === 'function' &&
(await PublicKeyCredential.isConditionalMediationAvailable().catch(() => false));
return canAutofill ? 'autofill' : 'modal';
}
Step 2 — Fix the annotation
<input name="username" autocomplete="username webauthn" autofocus />
Step 3 — Ensure the credential is discoverable at registration
// generateRegistrationOptions(...)
authenticatorSelection: { residentKey: 'required', userVerification: 'preferred' }
Step 4 — Issue the conditional call on load
Wire startAutofill() to DOMContentLoaded/idle as shown in the autofill implementation guide, never to a click.
Verification and Testing
Confirm the probe and the annotation in a headless or DevTools context:
// In the console on your sign-in page:
await PublicKeyCredential.isConditionalMediationAvailable(); // expect true on supported engines
document.querySelector('input[name=username]')?.autocomplete; // expect "username webauthn"
Use the Chrome DevTools WebAuthn tab to add a virtual authenticator with “resident key” enabled, register a passkey, then reload and focus the field — the passkey should appear. Toggling resident-key support off reproduces the empty-autofill case. A synthetic monitor should assert isConditionalMediationAvailable() === true on each target engine in CI.
Pitfalls
1. Hardcoding a support matrix in shipped code. Version tables rot; behaviour varies by OS and policy. Always call the probe at runtime and treat the table as documentation only.
2. Assuming API support implies autofill. They are independent capabilities — a browser can support WebAuthn modally but not conditionally.
3. Testing only in one profile. Private browsing and managed devices change probe results; test at least a normal and a private session per engine.
Related
- Conditional Mediation and Passkey Autofill UI — the parent implementation reference for the autofill ceremony
- Implementing Conditional UI with @simplewebauthn/browser — a library that wraps the probe and base64url handling
- Feature-Detecting Passkey and Conditional Mediation Support — the full three-probe detection strategy