Secure Biometric Authentication in Flutter: Are You Handling It Safely?

Gozie IhejirikaGozie Ihejirika
5 min read

Biometric authentication provides a convenient way for users to access your app securely. However, mishandling this process can compromise user data and security. Let’s go through how to securely implement biometric authentication in Flutter apps with backend integration.

Key Concepts

  • Never save User Credentials directly on the device.

  • Use Hashes and Device IDs to ensure secure biometric authentication.


Why Not Store and Authenticate Bio Credentials Locally?

At times I get asked by fellow developers, “If I store biometric credentials ‘securely’ on the device, can I not just validate locally then fire the login endpoint if successful?” My answer? A big NO!

Storing biometric login credentials on the device might sound convenient (and trust me—it is), but it’s a risky move. If the device gets lost, stolen, or hacked, those credentials could be exposed, putting the user’s account and sensitive data at serious risk. Even with encryption, it’s still vulnerable if someone skilled enough gains access to the device.


How To Securely Integrate Biometric Authentication in your Flutter App

  1. Set up device biometric security for your flutter app

  2. Generate and Securely Store a Unique Hash

    • Use packages like uuid and crypto (preferred) to generate hash ID: a unique identifier for each user account.

        import 'dart:convert'; // For UTF-8 encoding
        import 'package:crypto/crypto.dart'; // For SHA-256 hashing
      
        // Generates a SHA-256 hash for the given userId.
        String generateHash(String userId) {
          var bytes = utf8.encode(userId);
          return sha256.convert(bytes).toString();
        }
      
    • Store this hash securely using Flutter Secure Storage package.

        import 'package:flutter_secure_storage/flutter_secure_storage.dart';
      
        final FlutterSecureStorage storage = FlutterSecureStorage();
      
        Future<void> storeHash(String key, String hash) async {
          await storage.write(key: key, value: hash);
        }
      
        Future<String?> retrieveHash(String key) async {
          return await storage.read(key: key);
        }
      
    • Send the encrypted hash to the backend for storage.

      NOTE: It is quite common practice for the backend to generate this unique hash once the user triggers the endpoint to enrol for biometrics. In this case, the hash will be obtained from the backend and should then be stored securely.

  3. Set up biometric login endpoint

    Have the backend engineer create an endpoint for biometric login requests that accepts the hash ID, and a relevant additional identifier such as device ID.

  4. Validate during subsequent logins

    On each login attempt:

    • Retrieve the encrypted hash from secure storage

    • Together with the device ID, send it to the backend for validation

        import 'dart:convert';
        import 'package:http/http.dart' as http;
      
        // Authenticates a user using biometric data by validating the hash and device ID with the backend.
        Future<void> authenticateBiometric(String userId) async {
          // Retrieve the stored hash for the user
          String? hash = await retrieveHash(userId);
      
          if (hash != null) {
            // Prepare and send the authentication request to the backend
            final response = await http.post(
              Uri.parse('https://yourbackend.com/authenticate_biometric'),
              headers: {'Content-Type': 'application/json'},
              body: json.encode({
                'user_id': userId,
                'biometric_hash': hash,
                'device_id': 'unique_device_id' // Replace with actual device ID
              }),
            );
      
            if (response.statusCode == 200) {
              // Successful authentication: handle login or session creation
            } else {
              // Failed authentication: show error or retry
            }
          } else {
            // Handle the case where no hash is stored
          }
        }
      

      On receiving the hash and device ID, the backend checks:

    • Does the hash match the one stored on the server for this user?

    • Is the device ID associated with this user’s account?

  5. Handle edge cases

    • CASE 1: Hash and device ID match

      Allow user access into the app

    • CASE 2: Failed Biometric attempt on device

      The process stops on the device. The hash is never sent to the backend, and no login attempt is made.

    • CASE 3: Invalid Hash on backend

      If the hash doesn’t match, the backend should reject the login attempt and send an error response to the device.

    • CASE 4: Invalid Device ID on backend

      If the device ID is tied to another account, the backend should reject the login attempt with the user possibly being prompted to a manual login

      If the device ID is unregistered, the user may be allowed to register the device and proceed or prompted to additional security measures.

  6. Handling Lockouts [OPTIONAL]

    To ensure safety against brute force attacks or repeated invalid attempts, the backend or device can implement lockout mechanisms:

    • The backend can track invalid attempts per user or device and temporarily block further login attempts.

    • The device may also implement biometric lockout (e.g., requiring a PIN or password after multiple failed biometric attempts).


Is Secure Biometric Authentication Worth the hassle?

Absolutely! Taking the secure route with methods like hashes and device IDs might seem like extra work, but it’s worth every bit of effort. Here’s why: it ensures that sensitive biometric data isn’t directly stored on the device, which slims down the risk of exposure if the device gets lost or compromised. Instead of storing raw credentials, you’re creating a system that’s both user-friendly and highly secure.

By focusing on secure backend validation, you’re adding that extra layer of protection that goes beyond just the device. As a developer, it’s not just about making things work; it’s about building trust with your users. They probably won’t notice the tech behind the scenes, but they’ll value knowing their data is safe. And for you (yes, you!), it’s something to be proud of—building a login system that meets modern security standards, which, trust me, the App Store and Play Store will definitely be checking for.


What are your thoughts? Are there other secure methods you use for biometric authentication? Feel free to let me know what you think.

2
Subscribe to my newsletter

Read articles from Gozie Ihejirika directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gozie Ihejirika
Gozie Ihejirika