Authenticating users locally with the userVerification property
Enforcing local re-authentication using biometrics or device passcodes.
The WebAuthn specs allow developers to request a device authenticate a user locally before presenting a passkey. How this happens is device dependent, but devices typically use facial or fingerprint recognition if available on the device. Device passcodes may be used if biometrics are not supported.
By default, devices will attempt to authenticate a user, but may choose not to do so. You can instruct the device that it must authenticate the user by setting a userVerification property of required during registration or authentication:
import { authenticatePasskey } from "@passlock/browser";
await authenticatePasskey({ userVerification: "required", ...options }, { tenancyId })Your backend code must also check the userVerified flag on the resulting Principal:
import { Passlock } from "@passlock/server";
const passlock = new Passlock({ tenancyId, apiKey });const result = await passlock.exchangeCode({ code });
if (result.success && result.value.passkey?.userVerified) { // all good}import { Passlock } from "@passlock/server/unsafe";
const passlock = new Passlock({ tenancyId, apiKey });const result = await passlock.exchangeCode({ code });
if (result.passkey?.userVerified) { // all good}import { exchangeCode } from "@passlock/server";
const result = await exchangeCode({ code }, { tenancyId, apiKey });
if (result.success && result.value.passkey?.userVerified) { // all good}import { exchangeCode } from "@passlock/server/unsafe";
const result = await exchangeCode({ code }, { tenancyId, apiKey });
if (result.value.passkey?.userVerified) { // all good}User verification options
Section titled “User verification options”Choose from one of these options:
- preferred
This is the default during registration and authentication. The device will attempt to re-authenticate the user if technically possible, and frictionless. However if this can’t be achieved, for example a MacBook is in clamshell mode, the device will continue to present the passkey anyway.
- required
The device will attempt to use something like Face ID/Touch ID but if it can’t, it will fall back to prompting the user for their device password. Either way, the device won’t present the passkey unless the user has re-authenticated.
- discouraged
The device won’t re-authenticate the user, so this option results in the least friction.
Checking the verification status
Section titled “Checking the verification status”When preferred is used, it’s useful to know whether the user did actually re-authenticate locally. The resulting Principal includes a passkey.userVerified: boolean property to reflect the status.
Preferred, required or discouraged?
Which option to choose?
Section titled “Preferred, required or discouraged? Which option to choose?”In most cases we believe the default, preferred is best. You can always perform some additional verification e.g. sending a one-time code if the user didn’t re-authenticate locally.
However if the user has successfully authenticated you might choose to set a secure session cookie on their machine, in which case discouraged could strike a good balance between friction and security.
required is obviously the most secure and best used for security related operations, e.g. account or billing changes.