Biometrics are not the secret: lessons from banking apps
A fingerprint prompt feels like security, but it only unlocks something. What matters is what it unlocks, and where that thing lives.
Contents
Most mobile teams add Face ID or a fingerprint prompt, see the green tick, and call authentication done. After years of shipping banking and pension apps, I treat that tick as the least interesting part of the flow.
The prompt is a gate, not a key
A biometric check answers one question on the device: is the person holding this phone allowed to use it right now? It does not give your backend any proof. If your app reacts to a boolean returned by a JavaScript library, anyone who can hook that call, on a rooted or jailbroken device, can flip it to true.
If the result of a biometric check is a boolean in your app code, you have built a UI, not a security control.
Bind a key to the biometric
The robust pattern is to generate a key pair inside the Secure Enclave (iOS) or Android Keystore, mark it as requiring user authentication, and keep the private key non-exportable. The server stores the public key at enrolment. At login the app signs a server-issued challenge; the OS only releases the signature after a successful biometric.
- The private key never leaves secure hardware, so there is nothing to steal from app storage.
- A fresh server challenge on every attempt makes replay useless.
- The server verifies a signature, so a hooked boolean gets an attacker nowhere.
// Shape of the flow, independent of the library you pick
const challenge = await api.getChallenge(deviceId);
const signature = await secureKey.sign(challenge); // OS shows the biometric prompt here
const session = await api.verify({ deviceId, challenge, signature });Plan the unhappy paths first
Users change phones, add a new fingerprint, or disable biometrics entirely. When enrolled biometrics change, hardware-backed keys can be invalidated by design. Decide up front what happens next: usually a full re-authentication followed by re-enrolment, never a silent fallback to something weaker.
- Always offer a device passcode or PIN path with its own rate limiting.
- Invalidate the server-side public key when a user reports a lost device.
- Log failures server-side; the client cannot be trusted to report its own attacks.
What I take away
Treat biometrics as a convenience layer over real cryptography. The user gets one touch; the system gets a signed, replay-proof assertion. That combination is both better UX and better security, which is a rare thing to be able to say.