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.

Autofill capability matrix Three-column matrix showing that API support alone yields modal-only sign-in, API plus conditional mediation yields autofill-capable, and adding the webauthn annotation yields working passkey autofill. API only + Conditional mediation + webauthn annotation PublicKeyCredential ✓ isConditional… ✗ autocomplete ✗ PublicKeyCredential ✓ isConditional… ✓ autocomplete ✗ PublicKeyCredential ✓ isConditional… ✓ autocomplete ✓ Modal button only Autofill-capable, inert Passkey autofill works

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