I was looking at and implementing the webauthn spec this week including libraries and implementations. After reading quite a bit I believe that the registration ceremony as described almost everywhere is a lot more complicated than it should be for web use cases that do not want to limit supported authenticators. The full spec and protocol are certainly needed to cover all cases and the many corporate scenarios the spec references, but I believe for the vast majority of users there's a simpler version for use as a second factor in username/password auth or as a passkey in login.
Unfortunately most of the text is shrouded in a Standardese dialect I don't fully understand so please forgive any translation mistakes. My observations are:
Mediation and attestation features are mostly geared towards corporate environments that need to manage authenticators. These features seem unnecessary to most web use cases and create privacy concerns that would otherwise not exist. Most implementation should set attestation to "none" and completely ignore the attestation result as it will cause them more privacy concerns (having to report you store this in your PP) than it's worth. If you are regulated or otherwise concerned wrt. which type of authenticator is used you are not in the simple case I talk about.
Iff one does not use attestation ("none") and requires users have Level 2 capabilities in the browser, the first round trip can be entirely local. Registration can use a local random nonce generated by the browser - possibly even an all zero nonce. Some dynamic configuration concerns of the relaying party (your auth service) cannot be met but AFAICT most data in PublicKeyCredentialCreationOptions is effectively constant, meaning that requiring a frontend release to update them is very reasonable. The information contained (supported ciphers, RP information) is public because it comes back in the GET otherwise. While it seems at first that
excludeCredentialsshould probably still be populated by the server, the data is likely available on the page you would use to manage authenticators, i.e., a prior GET lists your current authenticators so the frontend most likely knows. In some other cases it's implicitly known (enable 2fa or signup know that it's empty). I was very surprised to find that the nonce is effectively unused except for the attestation response.
Assuming these two assumptions hold, and don't remove any load bearing security assumptions, the registration protocol can be simplified by a transformation in the browser to extract the data and make a single flat JSON POST to the register endpoint for a new authenticator (using a session which MUST of course be authenticated already, or a signup that passes a token in addition):
{
"displayName": // human assigned display name for this key
"id": "...", // base64UrlEncoded credential id
"cose": n1, // encryption number from the cost list
"pubkey": "...", // pubkey encoded as defined by encryption method
"initialCount": n2 // initial count as reported by the authenticator
"transports": [...] // list of transports for the credential
}
This would dramatically simplify the protocol as a whole. Display name comes from a regular form.
Assuming I trust the browser in this part of the flow (which, if compromised, later leaks the session) do I loose anything? It's not what the spec says happens, of course, but given the DRAMATICALLY simpler implementation and the removal of a painful CBOR dependency I would be willing to sacrifice that for the registration flow as long as core security invariants do not suffer.