fido-u2f for 21 of 26 U2F-style registrations, Safari on macOS returned
packed for 9 of 11. A server has to check both.State of the data: Passkeys Debugger registrations and logins through 1 November 2026, checked against FIDO Metadata Service BLOB 288. Samples per browser are small, so read the browser numbers as direction, not as market share. We will update the numbers as more registrations come in.
Banks, governments and enterprises that roll out security keys weigh how to implement and use them. Three decisions shape that work, and most guides blur them together: how the key is used, how the server identifies it and what the user sees when they touch it.
Experiment with passkey flows in the Passkeys Debugger.
This article separates them. It compares the security key as a second factor with the
passkey on a security key, explains the two attestation formats fido-u2f and packed,
shows why a PIN prompt appears although the site asked for none and defines what "only
FIDO Certified keys" means in practice. Every claim comes with real registration examples from the
Passkeys Debugger, so you can reproduce it with your own
key.
If you are choosing hardware, start with our best FIDO2 hardware security keys guide. If you want the short answer on terms, our FAQ on the difference between a YubiKey and a passkey covers it. This article is for the people who have to implement it.
A FIDO2 security key does not decide how it is used. The
relying party decides that through the options it sends to
navigator.credentials.create().
Security key as second factor vs passkey on a security key
The user enters username and password, then touches the key. The server asks for a
non-discoverable credential (residentKey: "discouraged") and no
user verification (userVerification: "discouraged").
The key stores nothing: its private key is wrapped into the credential ID that the server
keeps, so the number of accounts per key is unlimited.
This is how FIDO U2F has worked since 2014.
You can try exactly this setup in the debugger with the U2F-style security key preset. The security key as second factor guide walks through the test step by step.
The user touches the key and enters its PIN, no password. The server asks for a
discoverable credential
(residentKey: "required") and user verification (userVerification: "required"). The key
stores one credential slot per account, and the PIN or fingerprint on the key is the second
factor.
If the key has no PIN yet, the browser does not fail. It asks the user to set one first and only then creates the passkey. Every platform does this in its own words:
PIN setup dialogs in Chrome, Safari on macOS and iOS and Chrome on Android
That first PIN belongs to the key, not to your service. The user chooses it, the key stores it and nobody can reset it except by resetting the key, which deletes every passkey on it.
The matching debugger setup is the security key passkey preset.
NIST SP 800-63B-4 defines AAL3 as authentication with a phishing-resistant authenticator, a non-exportable key and two factors, with explicit authentication intent. Both setups fit:
AAL3 applies to the whole authentication process, not only the key: NIST also requires FIPS 140 validation, replay resistance and reauthentication limits. This article covers the authenticator side.
Synced passkeys do not reach AAL3, because their keys are exportable by design. That is the catch: the server has to know that it got a key and not a synced passkey. NIST says verifiers in federal enterprise systems should use attestation for that, everyone else may. In practice it is the only reliable signal.
Both setups above use clear values: "discouraged" for the second factor and "required"
for the passkey. Large relying parties that want maximum compatibility, Google among them,
deliberately use "preferred" instead. It avoids hard failures when a fingerprint sensor or
PIN is not available, and for security keys there are three more reasons:
"required" they would be locked out
completely. With "preferred" they still work as a second factor."required" forces PIN creation during registration. That is
friction, and it creates a PIN the user may later forget and nobody can reset."preferred" the same registration flow and UI serve both: Google accepts every key and
decides afterwards by the UV flag. With UV the key becomes a full passkey, without it the
key stays a second factor that only works together with a password.The price is that the browser, not the server, decides what gets created. We registered a Yubico Security Key NFC (firmware 5.8) with each combination in the Passkeys Debugger and repeated the Chrome tests with an older YubiKey 5 NFC that has a PIN set. Both keys gave the same credProtect levels:
residentKey | userVerification | Chrome | Safari |
|---|---|---|---|
discouraged | discouraged | No credProtect, no PIN | No credProtect, no PIN |
preferred | discouraged | credProtect level 2, PIN asked | - |
preferred | preferred | credProtect level 2 | Key without PIN: no PIN, user not verified, discoverability unknown |
required | discouraged | credProtect level 2, PIN asked | - |
required | preferred | credProtect level 3, PIN asked | - |
required | required | credProtect level 2, PIN asked | - |
Two things decide the outcome:
rk: true or false). With residentKey: "preferred" it is the
only way to know. Safari does not return it, so in Safari the server cannot tell what it
got.required with preferred, gets level 3: the key then demands its PIN at every login,
and Safari can fail to complete such logins. Our
credProtect article covers the details.With "discouraged" or "required" none of this is ambiguous. If you choose
"preferred", check the user verified flag after every registration and classify the key,
the way Google does.
Side note: a passkey as the second factor. Some services keep the password and accept a passkey as the second factor, on a security key or synced. The AWS console is the best-known example: IAM users sign in with their password and then confirm with a security key or a passkey. Teams usually choose this when a strong second factor is already in place and rebuilding the login around passkeys isn't worth it yet. It adds phishing resistance without changing the architecture. The assurance level depends on what the passkey is: a password plus a synced passkey reaches AAL2, a password plus a security key reaches AAL3. A passkey on a security key would be AAL3 even without the password, so dropping the password for those keys is the natural next step, but then the server has to run two login flows side by side, which is where implementation gets complicated.
At registration the key can send an attestation: a statement signed by the manufacturer
that says which model created the credential. The server only receives it when it asks for
it with attestation: "direct". Chrome then shows the user a prompt to share the key's make
and model.
How a server identifies a security key: AAGUID for packed, certificate key ID for fido-u2f
fido-u2f | packed | |
|---|---|---|
| Origin | FIDO U2F (2014), built only as a second factor | FIDO2: CTAP2 and WebAuthn (2018/19) |
| What the key signs | A fixed U2F message: app hash, challenge hash, key handle, public key | The full authenticator data plus the challenge hash |
| Model identifier | None, the AAGUID is all zeros | The AAGUID |
| Algorithms | ECDSA P-256 only | Several, e.g. ES256 and EdDSA |
| Flags and extensions | Not signed by the key | Signed: user present, user verified, backup eligible, extensions |
U2F was deliberately narrow: one message format, one algorithm, nothing else. FIDO2 added
passwordless login, PINs, discoverable credentials and extensions, all of which live in the
authenticator data that the old U2F signature does not cover. So FIDO2 needed a new format.
WebAuthn kept fido-u2f for backwards compatibility: when a browser talks to a key over the
old protocol (CTAP1), it wraps the U2F answer as fido-u2f.
The format is not your choice. It depends on the key and on which protocol the browser uses. Here are two real U2F-style registration examples from the same day, both from Yubico keys:
Two U2F-style registration examples from the Passkeys Debugger
In the debugger's Decoded panel the difference is visible at a glance. The fido-u2f
registration has an AAGUID of all zeros and is named by its attestation certificate key ID:
fido-u2f registration in the Passkeys Debugger: AAGUID all zeros, named by certificate key ID
The packed registration carries its AAGUID directly:
packed registration in the Passkeys Debugger: named by its AAGUID
Across all attested security key registrations in the debugger the split depends on the browser:
Which attestation format comes back for U2F-style requests, by browser
For passkey-style requests packed is almost universal. For U2F-style requests 23 of 54
registrations arrived as fido-u2f. A server that only handles packed would reject
genuine keys.
A fido-u2f key has no AAGUID, so the FIDO Metadata Service
lists it by the key ID of its attestation certificate. The key ID is not read from the
certificate, it is computed:
attStmt.x5c[0].SubjectPublicKeyInfo.subjectPublicKey.b95206228ead492b9e14504abefcb20546b0e53b.attestationCertificateKeyIdentifiers of the Metadata Service
entries. Here it matches "YubiKey 5 Series with NFC", certified L1 since May 2020.The certificate subject reads "Yubico U2F EE Serial 1155109599". That serial identifies a batch certificate shared by many thousands of keys of the same model, not the individual key, so attestation does not make a user trackable.
Password manager extensions replace navigator.credentials.create() in the browser and
offer to save a passkey in their own vault. For a security key rollout that is the wrong
result: the server receives a synced passkey, not a hardware key, and the AAL3 argument from
section 2.3 no longer holds. Three layers protect against it, and only the first one is a
real control:
"none" and are not listed as certified hardware. The check from section 5 rejects them,
whatever the extension does. This is the only layer that proves "hardware only"."security-key": tells the browser and extensions which authenticator you
expect. In our test with the 1Password browser extension, the hint was enough: 1Password
did not intercept the registration and the browser went straight to the key. A hint is a
request, not a rule, so treat it as a better user experience, not as protection.
Passkeys Cheatsheet. Practical guidance, rollout patterns and KPIs for passkey programs.
userVerification: "discouraged" is a request, not a guarantee. Whether the user is asked
for the key's PIN depends on the key, the browser and the operating system. The numbers below
count test sessions with credentials registered U2F-style, so each person and key counts
once:
Test sessions in which the key asked for a PIN although the site said discouraged
Three things decide the registration:
fido-u2f so often. In our test an older
YubiKey 5 NFC with a PIN set returned fido-u2f in Chrome without a PIN prompt.makeCredUvNotRqd option and create a non-discoverable
credential over CTAP2 without a PIN. Chrome then gets packed and no prompt. YubiKeys
with firmware 5.7 and later belong here.In WebKit's open-source code, the engine behind Safari, there is no fallback to U2F after an
error at registration, only at login. Safari falls back to U2F at registration only when a key
does not answer as a CTAP2 device when Safari first detects it. That is rare, which matches
the mostly packed results above: the two fido-u2f registrations came from keys that
returned packed in other Safari registrations, one of them minutes earlier.
At login, U2F-style credentials went through without a PIN in all seven Chrome sessions and all four Safari sessions on macOS. Safari falls back to U2F at login when CTAP2 finds no matching credential, so keys registered over U2F in another browser keep working.
Be careful with one trap when you read your own data. A discoverable credential registered in Chrome carries credProtect level 2 or 3 on the key (section 2.4). With level 3, or with level 2 and no credential ID in the request, the key itself demands the PIN at login, whatever the site asks for. Such logins look like a PIN prompt despite "discouraged", but they are a property of the credential, not of the browser. Most Windows logins in our data were of that kind; the one clean Windows login so far, in Firefox, went through without a PIN.
For a bank this matters more than any other detail. A customer who never set a PIN on the key, or forgot it, gets stuck at a prompt the bank cannot reset. Two consequences follow:
You can reproduce each case with your own key in the
U2F-style preset:
the response strip shows whether the user verified flag was set and which format came back.
When a key verified the user although you asked for "discouraged", the debugger explains
why under User verified, for example a discoverable credential or Windows. The
second factor guide shows the typical cases with screenshots.
Subscribe to our Passkeys Substack for the latest news.
"We accept FIDO Certified keys" sounds like a product label. Technically it is a lookup in the FIDO Metadata Service, and a key passes only when four things hold:
packed or by certificate key ID for fido-u2f.FIDO_CERTIFIED, _L1, _L1plus, _L2,
_L2plus, _L3 or _L3plus.REVOKED,
ATTESTATION_KEY_COMPROMISE or USER_KEY_REMOTE_COMPROMISE.Being listed is not the same as being certified. The Metadata Service also lists models with
NOT_FIDO_CERTIFIED or only a self-assertion.
448 attested security key registrations checked against the FIDO Metadata Service
Most rejections are correct: hobby and open-source keys, small vendors without metadata, and older U2F keys whose key IDs were never listed. Phones used over QR code are not security keys and are excluded from these numbers. The rejections that hurt are timing problems. A new key generation gets a new AAGUID, and the server only knows it once the Metadata Service lists it. Two YubiKey registrations with firmware 6.0 in our data were not in our October snapshot.
The same model can sit at two levels: YubiKey 5 Series with NFC appears 85 times as L1 and 49 times as L2 in our data, because each firmware generation has its own AAGUID and its own certification. Requiring L2 would have rejected another 40% of all registrations, most of them genuine YubiKeys.
A hand-kept allow list of AAGUIDs, key IDs and manufacturer roots works, but it is a frozen copy of the Metadata Service. It misses new certifications, new firmware AAGUIDs and above all revocations. The Metadata Service itself is free: a signed BLOB that the server downloads, verifies and refreshes about once a month.
Igor Gjorgjioski
Senior Product Lead, VicRoads
We hit 80% mobile passkey activation across 5M+ users without replacing our IDP.
See how VicRoads scaled passkeys to 5M+ users, alongside their existing IDP.
Read the case studySections 2 to 5 boil down to one list. The first two checks are part of every WebAuthn server; the policy checks decide whether "FIDO Certified" and "hardware only" actually hold for your rollout:
Registration checks for an AAL3 security key rollout
attestation: "direct" and
the hint "security-key". Use "discouraged" or "required"; with "preferred" check
the user verified flag and classify the key (section 2.4).packed and fido-u2f, chain to the manufacturer root. For
Yubico that means trusting both the older root and the newer root used from firmware 5.7.keyProtection says hardware or secure element.Not every identity platform exposes these checks. Some offer a minimum FIDO certification
level backed by the Metadata Service as a setting, others only collect the attestation and
leave the evaluation to you. Ask your vendor four questions: does it verify packed and
fido-u2f, can it require a certification level, can it reject BE=1 and can it keep an
allow list by AAGUID and key ID?
The Passkeys Debugger runs every check from this article on your own key: it decodes the attestation, names the model by AAGUID or certificate key ID, checks the chain and certification against the FIDO Metadata Service and shows the user present, user verified and backup flags. The presets for the U2F-style and passkey setups reproduce both uses in one click, and the second factor guide explains how to read the result.
In production, Corbado Observe shows the same signals across your real users: which keys register, in which format, where a PIN prompt appears and where customers drop off. That is the data you need to decide whether a stricter policy is worth the friction.
Get free passkey whitepaper for enterprises.
Both setups can support AAL3 when the authenticator and the complete authentication process meet the applicable NIST requirements. Attestation and metadata help verify the authenticator's properties, and attestation arrives in two formats that the server has to handle equally. "FIDO Certified" is a concrete Metadata Service check with a sensible minimum of L1, but FIDO certification alone does not establish AAL3 compliance. And "discouraged" user verification is a wish that Windows and some keys ignore, so the PIN belongs in your test plan and your support scripts.
It can be. NIST SP 800-63B-4 allows a password combined with a single-factor cryptographic device at AAL3, and the touch on the key provides the required authentication intent. The key must hold a non-exportable private key, which the server confirms through attestation and metadata, and the complete authentication process must meet NIST's other AAL3 requirements, such as FIPS 140 validation and reauthentication limits.
The browser talked to the key over the old U2F protocol. Chrome does that for
non-discoverable registrations when a CTAP 2.0 key has a PIN set, to avoid an unnecessary
PIN prompt. CTAP 2.1 keys usually return packed. Your server should accept both.
On Windows the browser hands security keys to Windows' own WebAuthn API, which shows its own dialog. Chrome's trick of falling back to the old U2F protocol is not available there, so an older CTAP 2.0 key with a PIN set will typically ask for it. Test Windows separately and make sure customers know their key's PIN.
Only with a hand-kept allow list of AAGUIDs, certificate key IDs and manufacturer roots. That list misses new certifications, new firmware and revocations, so the Metadata Service is the better source.
L1, combined with a metadata check for hardware key protection. Higher levels reject many genuine keys, because certification is tracked per firmware generation and many keys in use carry L1. L1 alone does not prove hardware protection, since it also covers software authenticators.
Related Articles
Table of Contents