Loading... Search articles

Search for articles

Sorry, but we couldn't find any matches...

But perhaps we can interest you in one of our more popular articles?
Flutter Local Authentication using Biometrics – Face ID and Touch ID / Fingerprint

Flutter Local Authentication using Biometrics – Face ID and Touch ID / Fingerprint

Feb 3, 2020

Written by Souvik Biswas

Local Authentication is an important verification step for several apps. This verification is fully done on device, so there is no way of leaking any sensitive information to any third-party servers.

This can also serve as an additional layer of security over the traditional sign-in methods (like using Email / Password or Google Sign In) to restrict access to some very sensitive information that should not get displayed to any other person except the user of the device. Some of the use-cases for local authentication may be for viewing the transaction history of the user or the authentication of any payment done through the app.

Apple App Store and Google Play Store already use Touch ID / Face ID and Fingerprint Authentication respectively to verify the transaction when a user purchases any app.

You can also use this Biometrics Security in your Flutter apps.

NOTE: This article uses the latest Flutter 2.0 stable release, with null safety enabled, for creating the sample app.

Getting Started

There is a nice plugin available for integrating Local Authentication in your Flutter app, known as local_auth. This uses the Platform APIs to get access to the device hardware, so there is no chance of leaking any private information from the device.

Let’s start by creating a new Flutter project, but first, let us know what’s your main difficulty when building apps?

Create project

Create a Flutter project by using the following command:

flutter create flutterfire_samples

Then open the project using your favorite code editor. For opening with VS Code you can use:

code flutterfire_samples

Flutter 2.0 has support for null safety in stable channel, but in order to use it inside the app you have to run a command for migrating the project to null safety.

Before running the migration command, check if all your current project dependencies support null safety by using:

dart pub outdated --mode=null-safety

Then, run the following command to migrate:

dart migrate

You can follow the migration guide here.

Import packages

Now, import the plugin by adding the following line to your project’s pubspec.yaml file:

dependencies:
  local_auth: ^1.1.0

You can get the latest version of the plugin available here.

Working on the UI

For this tutorial, I will be using one of my previously built projects known as flutterfire_samples.

Currently that project contains two layouts:

  1. SignInScreen
  2. UserInfoScreen

This is a simple project for implementing Firebase authentication & Google Sign In to your Flutter app.

Check out this article, if you want to get started with Firebase Auth and Google Sign In using Flutter.

We will be adding a SecretVaultScreen to this app, which will require biometric authentication to access.

The final app will have three layouts:

  1. SignInScreen
  2. UserInfoScreen
  3. SecretVaultScreen

Let’s get started by adding a button labeled Access secret vault to the UserInfoScreen that will route to the SecretVaultScreen.

The code for Access secret vault button (without biometric authentication):

ElevatedButton(
  style: ButtonStyle(
    backgroundColor: MaterialStateProperty.all(
      CustomColors.firebaseOrange,
    ),
    shape: MaterialStateProperty.all(
      RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(10),
      ),
    ),
  ),
  onPressed: () {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => SecretVaultScreen(),
      ),
    );
  },
  child: Padding(
    padding: EdgeInsets.only(top: 10.0, bottom: 10.0),
    child: Text(
      'Access secret vault',
      style: TextStyle(
        fontSize: 20,
        fontWeight: FontWeight.bold,
        color: Colors.white,
        letterSpacing: 2,
      ),
    ),
  ),
),

The SecretVaultScreen will contain an icon, a text, and a button (for going back to the previous screen).

The code for the SecretVaultScreen:

class SecretVaultScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: CustomColors.firebaseNavy,
      appBar: AppBar(
        elevation: 0,
        leading: Container(),
        backgroundColor: CustomColors.firebaseNavy,
        title: AppBarTitle(),
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.only(
            left: 16.0,
            right: 16.0,
            bottom: 20.0,
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Row(),
              Icon(
                Icons.lock_open,
                size: 60,
                color: CustomColors.firebaseGrey,
              ),
              SizedBox(height: 24.0),
              Text(
                'You have successfully accessed the secret vault. Leaving the vault is pretty easy, just go back to the previous screen by using the "Leave vault" button.',
                style: TextStyle(
                  color: CustomColors.firebaseGrey.withOpacity(0.8),
                  fontSize: 14,
                  letterSpacing: 0.2,
                ),
              ),
              SizedBox(height: 24.0),
              ElevatedButton(
                style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.redAccent),
                  shape: MaterialStateProperty.all(
                    RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                  ),
                ),
                onPressed: () => Navigator.of(context).pop(),
                child: Padding(
                  padding: EdgeInsets.only(top: 10.0, bottom: 10.0),
                  child: Text(
                    'Leave vault',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                      letterSpacing: 2,
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Local authentication overview

Before moving on to the actual implementation of the biometric authentication in our app, let’s take a look into the main things provided by the local_auth plugin.

First of all, import the local_auth package:

import 'package:local_auth/local_auth.dart';

Then you have create an instance of the plugin like this:

final LocalAuthentication localAuthentication = LocalAuthentication();

Some of the things that you can do with this plugin are:

  • Check if biometric authentication is supported by the device
  • Get a list of available biometric types
  • Authenticate user using biometrics or pin/passcode.

Check for biometric authentication

You can check if the device supports biometric authentication or if it can use the device credentials (pin/passcode lock):

bool isBiometricSupported = await localAuthentication.isDeviceSupported();

Now, if you want to verify whether biometric authentication is accessible from the app, you can check it using this:

bool canCheckBiometrics = await localAuthentication.canCheckBiometrics;

Retrieve list of biometric types

You can retrieve a list of biometric types that are supported by the device, using this:

List<BiometricType> biometricTypes =
          await localAuthentication.getAvailableBiometrics();

The list may contain one or more from the following types:

  • BiometricType.face
  • BiometricType.fingerprint
  • BiometricType.iris

Authenticate using biometrics or pin

You can perform the local authentication (that is using biometrics or pin/passcode) by using the following:

isAuthenticated = await localAuthentication.authenticate(
  localizedReason: 'Please complete the biometrics to proceed.',
);

If you want to restrict the user to biometric authentication (prevent authenticating using pin/passcode), you can set the biometricOnly parameter value to true:

isAuthenticated = await localAuthentication.authenticate(
  localizedReason: 'Please complete the biometrics to proceed.',
  biometricOnly: true,
);

In our implementation, we will need to check if biometric authentication is supported by the device, then let the user to only use biometrics to authenticate, and move on to the next screen.

Implement local authentication

We need to authenticate the user in the UserInfoScreen before proceeding to the SecretVaultScreen.

Define a new method in the Authentication class, present in the authentication.dart file, called authenticateWithBiometrics() where the entire logic of biometric authentication will be written.

class Authentication {
  static Future<bool> authenticateWithBiometrics() async {
    final LocalAuthentication localAuthentication = LocalAuthentication();
    bool isBiometricSupported = await localAuthentication.isDeviceSupported();
    bool canCheckBiometrics = await localAuthentication.canCheckBiometrics;

    bool isAuthenticated = false;

    if (isBiometricSupported && canCheckBiometrics) {
      isAuthenticated = await localAuthentication.authenticate(
        localizedReason: 'Please complete the biometrics to proceed.',
        biometricOnly: true,
      );
    }

    return isAuthenticated;
  }
}

The authenticateWithBiometrics() method will return a boolean indicating whether the biometric authentication is successful.

There are some optional parameters of this localAuthentication.authenticate() method that we can define:

  • localizedReason: The message to show the user while authentication.
  • useErrorDialogs: While set to true, it will check if fingerprint reader exists on the phone but there’s no fingerprint registered, the plugin will attempt to take the user to settings to add one.
  • stickyAuth: In normal circumstances the authentication process stops when the app goes to background. If stickyAuth is set to true, authentication resumes when the app is resumed.

Now, you can update the onPressed() method of the Access secret vault button to use the biometric authentication:

ElevatedButton(
  // ...
  onPressed: () async {
    bool isAuthenticated =
        await Authentication.authenticateWithBiometrics();

    if (isAuthenticated) {
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => SecretVaultScreen(),
        ),
      );
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        Authentication.customSnackBar(
          content: 'Error authenticating using Biometrics.',
        ),
      );
    }
  },
  // ...
),

If the authentication is successful then the user will navigated to the SecretVaultScreen, otherwise a SnackBar would be shown with an error message.

Setup for using biometrics

For Android:

Add this permission to the AndroidManifest.xml file present in the directory android -> app -> src -> main:

<uses-permission android:name="android.permission.USE_FINGERPRINT"/>

Update the MainActivity.kt file to use FlutterFragmentActivity instead of FlutterActivity:

import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity: FlutterFragmentActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)
    }
}

Fingerprint Authentication (Android)

For iOS:

For using FaceID on iOS, add the following line to the Info.plist file, this defines the message to be displayed when a user is prompted to authenticate:

<key>NSFaceIDUsageDescription</key>
<string>Biometric authentication for accessing secrets</string>

Testing the app on Simulator

For testing the app on iOS Simulator, you will need to enable FaceID or TouchID from the Simulator Hardware Settings.

  1. Run the app on Simulator
  2. Click on the Hardware in the top bar
  3. Go to Face ID or Touch ID, click on Enrolled
  4. Now, proceed with the biometric authentication
  5. When it prompts for the authentication, again go to Hardware -> Face ID or Touch ID.
  6. Here, you can choose between two options Matching Face or Non-matching Face (for Face ID) and Matching Touch or Non-matching Touch (for Touch ID), to simulate the authentication.

iOS Face ID

Conclusion

Local Authentication has a number of use-cases in apps and I hope this article will make it easier for you to integrate biometric authentication to your Flutter apps.

The GitHub repo of this app is available here.

How did you like that tutorial? Was is helpful? Let us know HERE.


Souvik Biswas is a passionate Mobile App Developer (Android and Flutter). He has worked on a number of mobile apps throughout his journey. Loves open source contribution on GitHub. He is currently pursuing a B.Tech degree in Computer Science and Engineering from Indian Institute of Information Technology Kalyani. He also writes Flutter articles on Medium - Flutter Community.


More articles by Souvik:

Latest articles

Show more posts