Passkeys are not a new protocol. They are Webauthn credentials that the operating system synchronises across a user’s devices. The wire format did not change. What changed is everything you had assumed around it.
One credential, several devices
Before passkeys, a Webauthn credential lived on exactly one authenticator. If your data model assumed one credential per device, it happened to be right. It no longer is: the same credential can now sign from a laptop, a phone and a tablet.
The practical consequence is on the signature counter. The specification lets an authenticator increment a counter at each use, so a server can detect a cloned key. Synchronised credentials do not maintain a meaningful counter, and often report zero. A server that rejects an authentication because the counter did not increase will lock out legitimate users.
Recovery becomes someone else’s problem, mostly
A lost security key used to mean a lost credential, and you needed a recovery path. A lost phone, with passkeys, means the user signs in on a new device with their platform account and the credential follows.
That does not remove your responsibility. It moves the weakest link to the platform account, and you no longer see it. Decide deliberately whether you still want a second registered credential, and say so at registration rather than discovering the question during an incident.
Attestation is mostly gone
Attestation lets you know what make and model of authenticator was used. It is what allows an organisation to require certified hardware. Synchronised passkeys generally provide no meaningful attestation.
If your policy needs attestation, you cannot accept passkeys for that population. If it does not, asking for attestation only adds a failure mode. Choose one, and write down why.
What to do in the code
- Store several credentials per user, and show them in a list the user can manage.
- Treat a counter of zero as normal, not as a cloning alarm.
- Set
residentKeytopreferredorrequiredif you want a username-less sign-in. - Use conditional UI so the browser offers the passkey in the login field itself.
Webauthn-Framework handles the protocol side of all of this. The decisions above are yours, and they are the part that is easy to get wrong.