Understanding WebAuthn credential protection policy
Understanding WebAuthn credential protection policy
This post assumes you're already familiar with the basics of<br>WebAuthn.
When creating a WebAuthn credential, you can specify whether it should be<br>discoverable using the residentKey option.
const credential = await navigator.credentials.create({<br>publicKey: {<br>authenticatorSelection: {<br>residentKey: "required",<br>// ...<br>},<br>// ...<br>},<br>});
However, the relying party cannot control when or how the credential can be<br>discovered. You may want it to become discoverable only after user<br>verification and hide the account’s existence from snooping users. This can<br>be especially important for security keys, where unlike devices or password<br>managers that usually require initial authentication, physical possession<br>alone is often sufficient to reveal registered credentials. To address this,<br>the CTAP 2.1 specification defines a new credential protection extension,<br>available through the credentialProtectionPolicy extension<br>input. CTAP is the specification that defines how platforms<br>(devices/browsers) and roaming authenticators (security keys) interact.
const credential = await navigator.credentials.create({<br>publicKey: {<br>extensions: {<br>credentialProtectionPolicy: "userVerificationRequired",<br>enforceCredentialProtectionPolicy: true,<br>},<br>// ...<br>},<br>});
If the default value userVerificationOptional is used, the<br>credential can be discovered and used without user verification. If<br>userVerificationOptionalWithCredentialIDList is used, the<br>credential cannot be discovered without user verification, but it can still<br>be discovered and used without user verification if the credential ID is<br>provided by the relying party. This matches the security of non-discoverable<br>credentials. Finally, userVerificationRequired indicates that<br>the credential cannot be discovered or used without user verification.
It’s important to highlight that the extension controls credential discovery<br>within the authenticator. It is still up to the relying party to verify<br>whether the assertion was made with user verification if they require it.
The related enforceCredentialProtectionPolicy extension input<br>configures what should happen if the authenticator does not support<br>credential protection policy. If set to true, the operation<br>will fail if it cannot create a credential at the specified security level.<br>Note that if you use a non-roaming authenticator that does not support<br>credentialProtectionPolicy but the browser does, the request<br>will be rejected. As such, this should only be set to true if<br>you want to allow roaming authenticators.
As for browser support for the extension inputs, Chrome and Firefox support<br>them, while Safari does not and will simply ignore them.
Browsers can also silently apply a default value if the relying party does<br>not specify one, which is specifically the case in Chrome.
If residentKey is preferred or<br>required, Chrome uses<br>userVerificationOptionalWithCredentialIDList. As noted, this<br>helps prevent someone with physical access to the authenticator from seeing<br>which accounts are registered on it.
If residentKey is required and<br>userVerification is preferred, Chrome will use<br>userVerificationRequired instead. The confusing part is that<br>this is not related to credential discovery, but rather serves as a safety<br>measure to enforce user verification. Chrome assumes the credential is<br>likely to be used as a single authentication step, since<br>preferred is commonly used even for passkey authentication.<br>However, because user verification is still optional and it is up to the<br>relying party to check for it, if the server does not properly enforce user<br>verification during authentication, someone could sign in as the user with<br>only physical access to the authenticator.
The specific behavior is documented in the<br>Chromium documentation.