In today’s digital world, passwords are becoming increasingly vulnerable to attacks like phishing, data breaches, and brute-force attempts. To enhance security and offer a better user experience, many web applications are turning to biometric authentication—a method that uses unique physical traits, such as fingerprints or facial recognition, to authenticate users.
Biometrics not only improve security but also make logging in more convenient for users. Thanks to the Web Authentication API (WebAuthn), integrating biometric authentication into your web application has never been easier.
For web applications, biometrics are typically used through devices like smartphones (with fingerprint or face scanning), laptops with fingerprint sensors, or external biometric scanners.
In this blog post, we’ll walk you through how to implement biometric authentication for your web app using WebAuthn. Let’s get started!
Why Implement Biometric Authentication?There are several benefits to using biometrics for web authentication:
- Enhanced Security: Biometric data is unique and hard to forge, making it a secure way to authenticate users.
- Convenience: No need to remember complex passwords. Users can log in with a simple scan of their finger or face.
- Phishing Resistance: With WebAuthn, biometric authentication is tied to the domain of the web application, making phishing attacks far less effective.
Web Authentication (WebAuthn) is part of the FIDO2 standard and allows web applications to offer passwordless authentication using public-key cryptography. This API enables web apps to authenticate users with external devices like biometric scanners, hardware security keys, or built-in device sensors.
Key features of WebAuthn:
- Passwordless authentication: Users log in using biometrics or security keys.
- Strong security: WebAuthn leverages public-key cryptography, where the private key is stored securely on the user’s device, and the public key is stored on the server.
- Phishing protection: Authentication is tied to the domain, preventing attackers from redirecting users to fake sites.
How Biometric Authentication Works with WebAuthn
WebAuthn works by allowing the browser to communicate with a biometric authenticator (such as a fingerprint reader or facial recognition sensor). The process involves two key steps:
- Registration: The user registers their biometric authenticator with the web app, which creates a public-private key pair.
- Authentication: When the user attempts to log in, the authenticator signs a challenge using the private key stored on their device, confirming their identity.
How to Implement Biometric Authentication Using WebAuthn
Let’s walk through the process of implementing biometric authentication using WebAuthn, from registration to login.
Prerequisites:- A modern browser that supports WebAuthn (Chrome, Firefox, Safari, Edge).
- A device with biometric capabilities (fingerprint scanner, facial recognition, etc.).
Step 1: User Registration (Client-Side)
In the registration phase, the user registers their biometric authenticator with your web application. This process involves creating a public-private key pair using the WebAuthn API.
Example: Registration (Client-Side)
async function registerUser() {
// Fetch registration options from the server
const options = await fetch('/webauthn/register', {
method: 'POST',
}).then(res => res.json());
// Convert challenge and user ID to byte arrays
options.publicKey.challenge = new Uint8Array(options.publicKey.challenge);
options.publicKey.user.id = new Uint8Array(options.publicKey.user.id);
// Create the public-private key pair using the biometric authenticator
const credential = await navigator.credentials.create({
publicKey: options.publicKey
});
// Send the credential to the server for registration
await fetch('/webauthn/register/response', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential)
});
}
In this code:
- We send a request to the server to get the registration options.
- The WebAuthn API (navigator.credentials.create()) is used to generate the key pair and communicate with the biometric authenticator.
- The public key is sent back to the server for future authentication.
Step 2: Server-Side Registration
On the server side, you need to handle the registration request and store the public key securely.
const { Fido2Lib } = require('fido2-lib');
// Initialize FIDO2 library
const fido = new Fido2Lib({
timeout: 60000,
rpId: "example.com",
rpName: "My Web App",
challengeSize: 64,
attestation: "direct"
});
// Endpoint to provide registration options
app.post("/webauthn/register", async (req, res) => {
const registrationOptions = await fido.attestationOptions();
res.json(registrationOptions);
});
// Endpoint to handle registration response
app.post("/webauthn/register/response", async (req, res) => {
const attestationResult = await fido.attestationResult(req.body);
// Store the public key and other necessary data in the database
res.json({ status: "ok" });
});
This server-side code uses the fido2-lib library to handle WebAuthn registration requests and store the public key for future authentication.
Step 3: User Authentication (Client-Side)
After a user has registered their biometric data, they can authenticate by using the same biometric authenticator. The WebAuthn API will generate a signed response to verify the user’s identity.
Example: Authentication (Client-Side)
async function loginUser() {
// Fetch authentication options from the server
const options = await fetch('/webauthn/login', {
method: 'POST',
}).then(res => res.json());
// Convert the challenge to a byte array
options.publicKey.challenge = new Uint8Array(options.publicKey.challenge);
// Prompt the user for biometric authentication
const credential = await navigator.credentials.get({
publicKey: options.publicKey
});
// Send the credential to the server for authentication
await fetch('/webauthn/login/response', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential)
});
}
This code initiates the authentication process, where the user is prompted to authenticate using their biometrics. The authentication response is then sent to the server for verification.
Step 4: Server-Side Authentication
On the server, we validate the authentication response and ensure the user is who they claim to be.
Example: Authentication (Server-Side with Node.js)
app.post("/webauthn/login", async (req, res) => {
const authenticationOptions = await fido.assertionOptions();
res.json(authenticationOptions);
});
app.post("/webauthn/login/response", async (req, res) => {
const assertionResult = await fido.assertionResult(req.body);
// Validate the authentication result
res.json({ status: "ok" });
});
The server-side code handles the authentication request and verifies the response from the client, completing the biometric authentication process.
Step 5: Checking for WebAuthn Support
Before implementing biometric authentication, it’s essential to check if the user’s browser supports WebAuthn and if their device has biometric capabilities.
if (window.PublicKeyCredential) {
console.log("WebAuthn is supported!");
} else {
console.log("WebAuthn is not supported on this browser.");
}
If WebAuthn isn’t supported, you can offer a fallback method like traditional password authentication or hardware security keys.
Security Considerations
- Privacy: WebAuthn ensures that biometric data never leaves the user’s device. The server only stores the public key, not sensitive biometric data.
- Fallback Options: Always provide users with a fallback authentication method (like a password or security key) in case biometric authentication fails.
- Multi-Factor Authentication (MFA): For higher security, consider using multi-factor authentication (MFA) by combining biometrics with another factor like a hardware security key or a PIN.