Password and OTP credentials are portable — they can be phished, replayed from a different device, or used by a credential-sharing household member. Device fingerprinting adds a possession factor derived from the actual browser and hardware environment, making a stolen credential set insufficient on an unrecognized device. CWE-287 (Improper Authentication) and OWASP A07 identify the gap between credential verification and device trust. NIST 800-63B IA-3 addresses device identification requirements. In financial fraud patterns, credential stuffing attacks typically originate from devices that have never logged into the account — fingerprinting flags this at first login from a new device and triggers step-up before any transaction is possible.
Medium because stolen credentials alone are sufficient to authenticate from an unrecognized device without triggering any additional verification, converting a phishing success directly into financial account access.
Integrate FingerprintJS or equivalent in src/lib/deviceFingerprint.ts and wire a step-up trigger for unrecognized devices in the login handler:
import FingerprintJS from '@fingerprintjs/fingerprintjs';
export async function getDeviceFingerprint(): Promise<string> {
const fp = await FingerprintJS.load();
const result = await fp.get();
return result.visitorId;
}
// In login handler
const fingerprint = await getDeviceFingerprint();
const knownDevices = await db.userDevices.findMany({ where: { userId } });
const isKnown = knownDevices.some(d => d.fingerprint === fingerprint);
if (!isKnown) {
// New device — block and require step-up before completing login
return requireStepUp(session, fingerprint);
}
Store enrolled device records in a user_devices table with fingerprint hash, enrollment timestamp, and a user-readable label (e.g., "MacBook Chrome"). Provide a UI for users to view and revoke trusted devices.
finserv-session-security.session-integrity.device-fingerprintingmedium"0 device fingerprinting implementations — new logins from unknown devices pass without additional verification"src/lib/deviceFingerprint.ts):
// lib/deviceFingerprint.ts
import FingerprintJS from '@fingerprintjs/fingerprintjs';
export async function getDeviceFingerprint() {
const fp = await FingerprintJS.load();
const result = await fp.get();
return result.visitorId; // Unique device identifier
}
// In login flow
const fingerprint = await getDeviceFingerprint();
const knownDevices = await db.userDevices.findMany({ where: { userId } });
const isKnownDevice = knownDevices.some(d => d.fingerprint === fingerprint);
if (!isKnownDevice) {
// New device — require step-up auth
return requireStepUp(session, fingerprint);
}