Not all users have biometric hardware — older laptops, shared workstations, and most desktop web browsers lack TouchID or FaceID. If biometric is the only step-up path on multi-device or web contexts, users without capable hardware are either blocked from completing financial operations or silently bypassed by the application. CWE-308 (Use of Single-Factor Authentication) applies when a required step-up factor is unavailable to a meaningful portion of users. PCI-DSS Req 8.4 requires that multi-factor authentication be available for all applicable users. At least two fallback methods — OTP-SMS, OTP-email, or TOTP — ensures step-up is universally enforced rather than selectively applied based on hardware.
Low because users without biometric hardware on multi-device contexts lose step-up protection entirely, effectively removing an authentication control for a predictable subset of the user population.
Add OTP and TOTP fallback paths in src/lib/stepUpAuth.ts for multi-device and web contexts:
export async function performMultiDeviceStepUp(
email: string,
phone: string
): Promise<boolean> {
const method = await promptStepUpMethod(
['otp-sms', 'otp-email', 'totp-app']
);
if (method === 'otp-sms') {
const otp = generateOTP();
await sendSMS(phone, `Your verification code: ${otp}`);
return verifyOTP(otp);
}
if (method === 'otp-email') {
const otp = generateOTP();
await sendEmail(email, `Your verification code: ${otp}`);
return verifyOTP(otp);
}
if (method === 'totp-app') {
return verifyTOTP(); // TOTP authenticator app
}
return false;
}
Expose at minimum two of the three methods so a user without a phone can use email OTP, and a user without email access can use a TOTP app.
finserv-session-security.step-up-auth.otp-sms-email-multidevicelow"1 of 2 required step-up methods on web — only WebAuthn, 0 OTP fallbacks for users without biometric hardware"src/lib/stepUpAuth.ts):
// lib/stepUpAuth.ts
export async function performMultiDeviceStepUp(email: string, phone: string) {
const method = await promptStepUpMethod(['otp-sms', 'otp-email', 'totp-app']);
if (method === 'otp-sms') {
const otp = generateOTP();
await sendSMS(phone, `Your step-up code: ${otp}`);
return await verifyOTP(otp);
} else if (method === 'otp-email') {
const otp = generateOTP();
await sendEmail(email, `Your step-up code: ${otp}`);
return await verifyOTP(otp);
} else if (method === 'totp-app') {
return await verifyTOTP(); // Authenticator app
}
}