v139 · Web APIs · WebAuthn
Passkey Manager
Register passkeys, then test all four mediation modes — immediate (new in Chrome 139), conditional, required, and the register flow — and see exactly when each one shows UI and what it returns.
register passkey
Calls navigator.credentials.create() with a generated user handle and challenge. Stores the credential ID in this page's in-memory list.
Not triggered yet.
sign in (immediate) NEW
Uses mediation: 'immediate' — shows picker right away if credentials exist; rejects with NotAllowedError if none. No waiting, no pending promise.
Not triggered yet.
sign in (conditional)
Uses mediation: 'conditional' — populates autofill suggestions silently. Promise stays pending until the user picks from the list.
Not triggered yet.
sign in (required)
Uses mediation: 'required' — always shows the credential picker modal, even if no credentials are stored.
Not triggered yet.
registered passkeys (this session)
0
No passkeys registered yet. Click "Register passkey" above.
mediation mode comparison
| mode | shows UI when | no credentials | user gesture needed | use case |
|---|---|---|---|---|
"immediate" ← new |
Credentials exist for this origin | Rejects: NotAllowedError |
Yes | Sign-in page: show picker or fall through to form |
"conditional" |
User focuses an input with autocomplete="webauthn" |
Promise stays pending | No (passive) | Autofill-style: populate suggestions alongside passwords |
"required" |
Always (modal) | Shows empty picker | Yes | Explicit "Use a passkey" button |
"optional" |
May show picker; resolves silently if possible | Resolves with null |
Depends | Silent re-auth for low-stakes actions |
code for each mode
// Register
const credential = await navigator.credentials.create({
publicKey: {
challenge: crypto.getRandomValues(new Uint8Array(32)),
rp: { name: 'Demo App', id: location.hostname },
user: { id: new Uint8Array(16), name: 'user@example.com', displayName: 'Demo User' },
pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
authenticatorSelection: { residentKey: 'required' },
},
});
// Immediate (new in Chrome 139)
try {
const cred = await navigator.credentials.get({
mediation: 'immediate',
publicKey: { challenge, rpId: location.hostname, allowCredentials: [] },
});
signInWith(cred);
} catch (err) {
if (err.name === 'NotAllowedError') showSignInForm();
}
// Conditional — passive, no user gesture required
await navigator.credentials.get({
mediation: 'conditional',
publicKey: { challenge, rpId: location.hostname, allowCredentials: [] },
});
// Required — always shows picker
await navigator.credentials.get({
mediation: 'required',
publicKey: { challenge, rpId: location.hostname, allowCredentials: [] },
});