User Verification
Prove to me who you claim to be!
User verification is a WebAuthn feature that confirms the identity of the actual user, not just possession of the authenticator. It answers the question: "Is the person using this authenticator really the authorized user?"
What is User Verification?
User verification requires the user to prove their identity through:
🔐 PIN code - Entering a secret code on the authenticator
👆 Biometric recognition - Fingerprint, facial recognition, iris scan
🔑 Password entry - Entering a password on the device
🔒 Combined methods - Touch + PIN, or other combinations
The goal is to distinguish individual users and ensure that the person authenticating is the legitimate account owner, not just someone who stole or found the authenticator.
User Presence vs User Verification
It's important to understand the difference:
What it proves
Someone is there
Who is there
How
Simple button press, touch
PIN, biometric, password
Flag
UP flag in authenticator data
UV flag in authenticator data
Security level
Basic
Enhanced
Requirement
Always required
Optional (configurable)
User Presence is always enforced by WebAuthn - it simply confirms someone physically interacted with the authenticator (like touching a button).
User Verification goes further by confirming the specific identity of that person.
User Verification Requirements
You can configure the user verification requirement when creating credential options. There are three possible values:
required
requiredThe operation requires user verification and will fail if the authenticator does not set the UV flag.
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\PublicKeyCredentialCreationOptions;
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED
);
$publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create(
rp: $rpEntity,
user: $userEntity,
challenge: random_bytes(32),
authenticatorSelection: $authenticatorSelection
);Use when:
High-security applications (banking, healthcare)
Privileged operations (admin actions, financial transactions)
Compliance requirements mandate biometric authentication
preferred
preferredThe operation prefers user verification but will not fail if the authenticator doesn't provide it.
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED
);This is the recommended default for most applications. It uses user verification when available but doesn't exclude authenticators that don't support it.
Use when:
General-purpose authentication
You want security when available without limiting device compatibility
Balancing security and user experience
discouraged
discouragedThe operation discourages user verification to minimize disruption to the user flow.
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
);Use when:
Low-risk operations
Maximizing speed and convenience
Simple presence verification is sufficient
Testing or demonstration purposes
Checking the UV Flag
After authentication or registration, verify whether user verification actually occurred:
// During assertion validation
$authenticatorData = $publicKeyCredential->response->getAuthenticatorData();
// Check if user was verified
$userVerified = $authenticatorData->isUserVerified();
if ($userVerified) {
echo "User identity verified via PIN, biometric, or password";
} else {
echo "Only user presence confirmed (button press/touch)";
}
// You can also check the flags directly
$flags = $authenticatorData->getFlags();
$uvFlag = ($flags & 0x04) !== 0; // UV flag is bit 2Practical Examples
Example 1: High-Security Banking App
// Require user verification for all operations
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED
);
// This ensures only authenticators with PIN/biometric can be used
$options = PublicKeyCredentialCreationOptions::create(
rp: $rpEntity,
user: $userEntity,
challenge: random_bytes(32),
authenticatorSelection: $authenticatorSelection
);Example 2: General Web Application
// Prefer user verification but don't require it
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED
);
// Works with all authenticators, uses UV when available
$options = PublicKeyCredentialCreationOptions::create(
rp: $rpEntity,
user: $userEntity,
challenge: random_bytes(32),
authenticatorSelection: $authenticatorSelection
);Example 3: Fast Re-authentication
// For quick re-auth on already logged-in user, discourage UV
$options = PublicKeyCredentialRequestOptions::create(
challenge: random_bytes(32),
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
);Device Capabilities
Different authenticators have different user verification capabilities:
Touch ID / Face ID (macOS/iOS)
Fingerprint, facial recognition
Windows Hello
Fingerprint, facial recognition, PIN
Android Platform
Fingerprint, facial recognition, pattern, PIN
YubiKey 5 Series
PIN (must be set up)
YubiKey Bio
Fingerprint
Basic security keys
None (only user presence)
Best Practices
For Registration
Use
preferredas default - Balances security and compatibilityUse
requiredfor sensitive accounts - Banking, healthcare, admin accountsInform users - Explain that biometric/PIN improves security
For Authentication
Match registration settings - If registered with
required, authenticate withrequiredAllow
preferredfor convenience - Users can choose faster auth if device supports itDocument your policy - Make clear what level of verification is expected
Conditional Logic
// Require UV only for privileged operations
$isAdminAction = ($user->getRole() === 'ROLE_ADMIN');
$userVerification = $isAdminAction
? AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED
: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED;
$options = PublicKeyCredentialRequestOptions::create(
challenge: random_bytes(32),
userVerification: $userVerification
);Common Issues
Issue: "User verification not supported"
Cause: User's authenticator doesn't support UV, but you set required.
Solution: Use preferred instead, or provide alternative authentication methods.
Issue: Users complain about entering PIN repeatedly
Cause: Setting required for all operations, even low-risk ones.
Solution: Use discouraged or preferred for routine operations, save required for sensitive actions.
Security Considerations
✅ Higher assurance - UV provides stronger identity verification than UP alone
✅ Phishing resistant - Even if an attacker steals the authenticator, they need the PIN/biometric
⚠️ Not foolproof - Biometric systems can have false positives; PINs can be observed
⚠️ Device dependent - Relying solely on UV limits compatible devices
Constants Reference
The library provides convenient constants in the AuthenticatorSelectionCriteria class:
use Webauthn\AuthenticatorSelectionCriteria;
// For registration (attestation)
AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED
AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED
AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
// These same constants can be used for authentication (assertion)
// in PublicKeyCredentialRequestOptionsSee Also
Authenticators - Learn about different authenticator types
Authenticator Selection Criteria - Detailed configuration
Ceremonies - How user verification fits into the authentication flow
WebAuthn Specification - User Verification - Official specification
Last updated
Was this helpful?