There are 2 separate things.
- Federated vs. Webauthn
Webauthn is not federated credential so you cannot just outsource them authentication like to an email provider or to apple, google etc. If you want to do that, you need to do that but you are already doing it and want something different, I guess? A had the feeling you want to get rid of federated but you want to keep outsourcing authentication, which is: federated (have the cake and eat it too or the other way around).
By federated your user is the owner of the email account or of the apple, google etc. account. No need to register because your user registered elsewhere. You get an id and an authentication service from others...
If you want webauthn, you need to authenticate yourself and register your user with create() first. On the server you store internal uid -> pid (passkey id which is called user id and user handle in the webauthn specs but it is actually a passkey id) and pid->public key. On the client side the pass manager (authenticator) stores: xyz.com -> {pid, private key} and sync it. I will call passkey the private key... some use it for the key pair.
Your users anonymous identity is the pid or rather a set of pids since it seems to go to the direction of creating more than one passkeys per account instead of solving the syncing of one passkey... you verify the identity or the pid with the verification key which is the public key.
By a sign in process get() is called and a random dynamic server challenge is signed by the pass manager with the private key and you verify the signature and hence the pid as identity with the corresponding public key on your server.
It is very nice math and anonymous and fishing resistant etc. but it is you(!) that verifies the identity and not someone else! So first you have to create it, even worse: as of now on apple, google, microsoft and later on linux platforms separate ones (a set of pids, a set of public keys).
I am not sure it is not the very thing that will bury webauthn passkeys since it will be a maintenance hell for users/website owners in the long run. A security problem too since plenty of users will start to create hordes of passkeys without ever used a pass manager before or without knowing that they actually use one... Webauthn passkeys require a pass manager and there are people who do not want to use one or will not know they use one... it might work in the beginning but it implies that people will all be able to handle a pass manager. I am not sure of that...
- Usernameless internal logic with 2 required usernames
I think you are correct and it is cool you do not use usernames and email address is an option. The sad thing is, webauthn is logically usernameless (pid is the real anonymous identity and pass manager use the pid, the 2 usernames are just labels in case more than one account per domain) but the webauthn spec has a logical misstep by requiring not just one but 2 username fields... I personally think it is very bad and at least they should make these fields optional. The default thinking should be that one real person has one account per domain and then the whole UX would be super nice and simple: "create passkey for xyz.com" and "use your passkey for xyz.com" (one entry per domain per pass manager without any usernames, with simplified UX with less unnecessary text).
If someone creates more than one account which is possible, this person has the responsibility to manage pass manager entry labels and differentiate the accounts (one optional note field instead of 2 username fields would suffice...). It is not common thinking but extremely logical :) Webauthn should totally abolish username fields since they added pid (user id, user handle - they manage to call it 2 different things). It is not the web site owners responsibility to micromanage pass manager labels in the users pass manager. The conflict of having 2 accounts per domain is actually a misgeburt and created by the user and it is a design smell (like websites using emails as usernames and users want a burner account too which they would not want in the first place if accounts that do not require real identity would stop gathering personally identifiable information).
More about this topic here:
User name and displayName change for existing passkey
All in all, webauthn is not federated, it is you who has to check the identity by "sign in" using get() and for this you need a first step to register an anonymous identity via create().
With the username mess I think you have the right feeling, it should not be there.