Use case
Account takeover: step up verification when a login looks wrong
In an account takeover an attacker signs in with a stolen or guessed password. A correct password proves little, so the login needs more evidence. FingerPrint Checker adds it: whether this account has used this device before, how many countries the device appeared from lately, whether the visit comes through a VPN, a proxy or Tor, and whether a bot is driving it. You decide when to ask for a second step.
The threat
Stolen credentials are cheap, and they are tried at scale by scripts. The attacker types the right password, so the sign-in looks valid. What differs is the context: a device the account has never used, a sudden jump between countries, an anonymising network, automation.
Evidence you get at login
These are signals to weigh, not verdicts. A traveller or a person on a company VPN can trigger some of them, and a determined attacker can avoid others. They tell you when to ask a login for more, not that an attack is certain.
- New device for this account: compare the visitor ID with the IDs you have seen for that user.
- Several countries in a short window for one device, from the velocity counts.
- VPN, proxy and Tor results for the visit.
- The bot result: a good bot, a bad bot or not detected, with the kind of automation seen.
- Signs that the browser was altered to hide the visitor.
How to add it
Identify the visitor on the sign-in page and send the event ID to your backend with the credentials. Your server reads the event with your secret key and decides: continue, ask for a one-time code or another second factor, or hold the sign-in for review. Never trust a visitor ID or a score that the browser reports itself.
Keep the friction where it belongs
Most logins come from a known device and need nothing extra. Ask for step-up verification only on a risky combination, and remember the new device once the user has passed it.
Step up on a risky login
// Server side, after the password check passed
if (!/^ev_[0-9A-HJKMNP-TV-Z]{20}$/.test(eventId)) throw new Error('bad eventId'); // fail closed: ask for the second step
const r = await (await fetch(`https://api.fpchecker.com/v1/events/${eventId}`, {
headers: { 'X-Secret-Key': process.env.FPCHECKER_SECRET_KEY },
})).json();
if (!r.visitorId) throw new Error(r.error ?? r.reason); // fail closed: ask for the second step
if (r.eventId !== eventId || !(Date.now() - Date.parse(r.createdAt) <= 5 * 60_000)) throw new Error('stale or reused event'); // wrong or old event
if (!(await markEventUsed(eventId))) throw new Error('stale or reused event'); // your store: true only the first time
const s = r.signals ?? {};
const knownDevice = await db.hasDevice(user.id, r.visitorId); // your own table of IDs seen per user
const risky =
!knownDevice ||
(s.velocity?.distinctCountry?.['1h'] ?? 0) > 1 ||
s.vpn?.result === true || s.proxy?.result === true || s.ip?.tor === true ||
s.bot?.result === 'bad';
return risky ? requireSecondFactor(user) : finishLogin(user);Frequently asked questions
Is this a complete defence against takeover?
No tool does on its own. It gives you evidence at the moment of login, so that your step-up verification fires when it should and stays out of the way when it should not.
What if a signal is unknown?
Unknown is null, not false. Decide in advance what your server does with null; for a sensitive account, treating it as a reason to ask again is the safe choice.
Will it flag customers who use a VPN?
A VPN is evidence, not proof. Combine it with the other signals, and ask for a second factor instead of refusing the login.
Try it on your own sign-up flow
Every new account gets 2,000 free checks. No card required.