This document discusses what userVerification
is in WebAuthn, and the browser behaviors that result when userVerification
is specified during passkey creation or authentication.
What is "user verification" in WebAuthn?
Passkeys are built on public key cryptography. By creating a passkey, a public-private key pair is generated, the private key is stored by the passkey provider, and the public key is returned to the relying party's (RP) server to store. The server can authenticate a user by verifying a signature signed by the same passkey using the paired public key. The "user present" (UP) flag on a public key credential proves that someone interacted with the device during the authentication.
User verification is an optional layer of security that seeks to assert that the correct person was present during authentication, not just some person, as user presence asserts. On smartphones, this is usually done by using the screen-lock mechanism, whether that be a biometric or either a PIN or password. Whether user verification was performed is reported in the "UV" flag that is returned in the authenticator data during passkey registration and authentication
How UP and UV are validated on the server
The user presence (UP) and user verified (UV) boolean flags are signaled to the server in the authenticator data field. During authentication, the contents of the authenticator data field can be validated by verifying the signature using the stored public key. As long as the signature is valid, the server can consider the flags genuine.
On passkey registration and authentication, the server should examine that the UP flag is true
, and whether the UV flag is true
or false
, depending on the requirement.
Specifying the userVerification
parameter
Per the WebAuthn specification, the RP can request a user verification with a userVerification
parameter on both credential creation and assertion. It accepts 'preferred'
, 'required'
, or 'discouraged'
which respectively means:
'preferred'
(default): Using a user verification method on the device is preferred, but can be skipped if it's not available. The response credential contains a UV flag value oftrue
if user verification was performed, andfalse
if UV was not performed.'required'
: Invoking a user verification method available on the device is required. If one is not available, the request fails locally. This means the response credential always returns with the UV flag set totrue
.'discouraged'
: Using a user verification method is discouraged. However, depending on the device, user verification may be performed anyway, and the UV flag can containtrue
orfalse
.
Sample code for passkey creation:
const publicKeyCredentialCreationOptions = {
// ...
authenticatorSelection: {
authenticatorAttachment: 'platform',
residentKey: 'required',
requireResidentKey: true,
userVerification: 'preferred'
}
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
Sample code for passkey authentication:
const publicKeyCredentialRequestOptions = {
challenge: /* Omitted challenge data... */,
rpId: 'example.com',
userVerification: 'preferred'
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions
});
Which option should you choose for userVerification
?
The userVerification
value you should use depends on your application requirements, as well as your user experience needs.
When to use userVerification='preferred'
Use userVerification='preferred'
if you prioritize user experience over protection.
There are environments where user verification is more friction than protection. For example, on macOS where Touch ID is not available (because the device doesn't support it, it's disabled, or the device is in clamshell mode), the user is asked to enter their system password instead. This causes friction, and the user may abandon authentication entirely. If eliminating friction is more important to you, use userVerification='preferred'
.
With userVerification='preferred'
, the UV flag is true
if user verification is successfully performed, and false
if user verification is skipped. For example, on macOS where Touch ID is not available, it asks the user to click a button to skip user verification, and the public key credential includes a false
UV flag.
The UV flag can then be a signal in your risk analysis. If the sign-in attempt seems risky due to other factors, you may want to present additional sign-in challenges to the user if user verification was not performed.
When to use userVerification='required'
Use userVerification='required'
if you think both UP and UV are absolutely necessary.
A downside of this option is that the user may experience more friction when signing in. For example, on macOS where Touch ID is not available, the user is asked to enter their system password.
With userVerification='required'
, you can ensure that user verification is performed on the device. Make sure the server verifies that the UV flag is true
.
Conclusion
By leveraging user verification, passkey-relying parties can gauge the likelihood of the device owner signing in. It's their choice whether to require user verification, or make it optional depending on how critical the fallback sign-in mechanism impacts the user flow. Make sure the server checks the UP flag and UV flag for passkey user authentication.