Categories:
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

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.

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.

First of all, create the Flutter project with AndroidX support because the plugin uses AndroidX and if your project is not AndroidX compatible then you will get a lot of errors.

Creating the project

Creating project using Flutter 1.12 and above (by default AndroidX compatible):

flutter create sign_in_flutter

For Flutter versions below 1.12:

flutter create --androidx sign_in_flutter

If you have an existing Flutter project which is not AndroidX compatible then you can follow this official AndroidX Migration guide.

Importing packages

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

local_auth: ^0.6.1

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 sign_in_flutter.

The project has two screens:

  1. Sign In Screen
  2. Profile Screen

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 Transaction Overview Screen to this app, which will require biometric authentication to access.

The final app will have three screens:

  1. Sign In Screen
  2. Profile Screen
  3. Transaction Overview Screen

Let's get started by adding a button labeled Transaction Overview which will route to the Transaction Overview Screen.

The code for Transaction Overview button (without biometric authentication):

RaisedButton(
  onPressed: () async {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => TransactionScreen(),
      ),
    );
  },
  color: Colors.deepPurple,
  child: Padding(
    padding: const EdgeInsets.all(8.0),
    child: Text(
      'Transaction Overview',
      style: TextStyle(fontSize: 25, color: Colors.white),
    ),
  ),
  elevation: 5,
  shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(40)),
)

The demo Transaction Overview Screen will contain only a Image and a Text widget.

The code for demo Transaction Overview Screen:

class TransactionScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.deepPurple[300],
        elevation: 0,
      ),
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [Colors.deepPurple[300], Colors.deepPurple[900]],
          ),
        ),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Image.asset('assets/money.png'),
              SizedBox(height: 40),
              Text(
                'Transaction Overview',
                style: TextStyle(fontSize: 30, color: Colors.white),
              ),
              SizedBox(height: 50),
            ],
          ),
        ),
      ),
    );
  }
}

Adding Local Authentication

We need to authenticate the user in the Profile Screen before proceeding to the Transaction Overview Screen.

So, import the local_auth package in the Profile Screen:

import 'package:local_auth/local_auth.dart';

Create an instance of the LocalAuthentication:

final LocalAuthentication _localAuthentication = LocalAuthentication();

Now, we will be implementing three methods:

  1. _isBiometricAvailable(): For checking if any type of biometric authentication hardware is available in the device.
  2. _getListOfBiometricTypes(): To get the list of available biometric types.
  3. _authenticateUser(): For authenticating the user using biometrics.

All these methods will return a Future<> object and

class ProfileScreen extends StatefulWidget {
    @override
    _ProfileScreenState createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {
    final LocalAuthentication _localAuthentication = LocalAuthentication();

    // To check if any type of biometric authentication
    // hardware is available.
    Future<bool> _isBiometricAvailable() async {
      // ...
    }

    // To retrieve the list of biometric types
    // (if available).
    Future<void> _getListOfBiometricTypes() async {
      // ...
    }

    // Process of authentication user using
    // biometrics.
    Future<void> _authenticateUser() async {
      // ...
    }

    @override
    Widget build(BuildContext context) {
      // ...
    }
}

_isBiometricAvailable()

We will be using canCheckBiometrics (which returns a boolean value) of the LocalAuthentication class to retrieve if any biometric hardware is available.

The code for checking if any biometric hardware is present in the device:

  Future<bool> _isBiometricAvailable() async {
    bool isAvailable = false;
    try {
      isAvailable = await _localAuthentication.canCheckBiometrics;
    } on PlatformException catch (e) {
      print(e);
    }

    if (!mounted) return isAvailable;

    isAvailable
        ? print('Biometric is available!')
        : print('Biometric is unavailable.');

    return isAvailable;
  }

_getListOfBiometricTypes()

The method getAvailableBiometrics() can be used to retrieve the list of available biometric types.

The code for this method:

  Future<void> _getListOfBiometricTypes() async {
    List<BiometricType> listOfBiometrics;
    try {
      listOfBiometrics = await _localAuthentication.getAvailableBiometrics();
    } on PlatformException catch (e) {
      print(e);
    }

    if (!mounted) return;

    print(listOfBiometrics);
  }

_authenticateUser()

In this method, we will be authenticating the user using the biometric hardware available. We will be using the method authenticateWithBiometrics(), which returns a boolean value, of the LocalAuthentication class. There are some optional parameters of this 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.

If the authentication is successful then the user is navigated to the Transaction Overview Screen.

  Future<void> _authenticateUser() async {
    bool isAuthenticated = false;
    try {
      isAuthenticated = await _localAuthentication.authenticateWithBiometrics(
        localizedReason:
            "Please authenticate to view your transaction overview",
        useErrorDialogs: true,
        stickyAuth: true,
      );
    } on PlatformException catch (e) {
      print(e);
    }

    if (!mounted) return;

    isAuthenticated
        ? print('User is authenticated!')
        : print('User is not authenticated.');

    if (isAuthenticated) {
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => TransactionScreen(),
        ),
      );
    }
  }

Now, update the onPressed method of the Transaction Overview button. Rather than Navigating to the Transaction Overview Screen, use the biometrics to verify the user.

  RaisedButton(
    onPressed: () async {
      if (await _isBiometricAvailable()) {
        await _getListOfBiometricTypes();
        await _authenticateUser();
      }
    },

  // ...

  ),

Setup for using Biometrics

For Android:

Add this permission to the AndroidManifest.xml file:

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

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

import android.os.Bundle;
import io.flutter.app.FlutterFragmentActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterFragmentActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
  }
}
Fingerprint Authentication (Android)
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>Please authorize to use the app</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 Touch ID
iOS Touch 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. Checkout the branch local_auth of the project sign_in_flutter.


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: