If we look at the WebAuthn specification, then, during the "registration" ceremony, the authenticator generates a new key-pair and a unique user-id. Then the public-key and the unique user-id are sent to the relying party (server), together with some attestation statement. The relying party will store the public-key and the user-id in their database and associate them with the user's account.
Later, during the "authentication" ceremony, the relying party sends the user-id back to the client, together with a random challenge. The authenticator uses the user-id to find the correct private-key (the one that it generated during the "registration" ceremony), so that it can sign the challenge with that key. Finally, the signed challenge is sent to the relying party, where the signature will be verified with the corresponding public-key (the one that was stored during the "registration" ceremony).
https://www.w3.org/TR/webauthn-2/images/webauthn-registration-flow-01.svg
https://www.w3.org/TR/webauthn-2/images/webauthn-authentication-flow-01.svg
This clearly requires that the authenticator has stored the private-key in its internal memory. Otherwise it wouldn't be able to look up the private-key based on the given user-id and use it to sign the challenge. At least, I cannot see that the client/authenticator gets anything back from the relying party, except for the user-id. So, actually, the authenticator must store all private-keys (and their corresponding user-id) that it ever generated, so that future logins will be possible.
Now here is what confuses me: Some sources claim that, normally, the private-key is actually stored on the relying party (in an encrypted form), not in the authenticator. And that only so-called "resident" keys (discoverable credentials) are stored locally in the authenticator. But, if that was the case, then how do "normal" (not resident) keys really work? I can see nothing in the WebAuthn API where the client or the authenticator would send an encrypted private-key to the relying party, as part of the registration. Also, I can see nothing in the WebAuthn API where the relying party would send an encrypted private-key back to the client/autenticator, as part of the authentication...
Discoverable Credentials / Resident Keys
WebAuthn enables high assurance multi-factor authentication with a passwordless login experience. One of the things that enables this is what is called Discoverable Credentials, also referred to as resident keys.
Discoverable Credential means that the private key and associated metadata is stored in persistent memory on the authenticator, instead of encrypted and stored on the relying party server.
So, where is the "normal" (not resident) WebAuthn private-key really stored? If it is stored on the relying party server, as some sources claim, where/when exactly is an encrypted private-key exchanged between the authenticator and the relying party? Which API function/object does this?