Biometric Authentication in Flutter(Face ID/ Fingerprint)
Published: 2023-08-08 11:41:25

One of the essential thing in mobile application is security especially if your building a finance app, and one of the most used security measure is using biometric authentication, which means you verify user by using phone face ID, Fingerprint or phone password.

Authentication using biometrics can be challenging. As it requires a good understanding of the underlying platform's APIs and implementation details.

But also, not all devices support biometric authentication, which requires developers to handle when user device does not have biometric authentication.

Luck us flutter provides a package for this [local_auth](https://pub.dev/packages/local_auth)

This package implements biometric security with Face ID and Touch ID/Fingerprint.

😜 Let's dive in and learn how to implement it in our flutter app.

But wait we need to go through the following steps to implement this.

- Setting up the project
- Checking biometric availability
- Requesting biometric authentication
- Handling authentication results

## Setting up the project

First thing we need to install local authentication to our project.

```
dependencies:
   local_auth: ^2.1.6
```

Then run the following command to install this plugin, though if your using vsCode you do not need to run this.

```
flutter packages get
```

**Android Setup**

Add following line under manifest tag in `AndroidManifest.xml`

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

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

```
//add this import
import io.flutter.embedding.android.FlutterFragmentActivity

//Chnage to FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity() {
      // ...
}
```

**IOS Setup**

Add following code to your `info.plist`

```
<key>NSFaceIDUsageDescription</key>
<string>Why is my app authenticating using face id?</string>
```

😮‍💨 we're done with the setup now let's write some code.

## Checking biometric availability

We need to make sure that user phone has biometric authentication before we request to use it.

To check if biometric authentication is available or not. We use `canCheckBiometrics` method on the `LocalAuthentication` class.

Create a `statefull` widget called `LoginScreen` and add the following functions

```
import 'package:local_auth/local_auth.dart';

final LocalAuthentication _localAuth = LocalAuthentication();

//Check if biometric auth is available
Future<boolean> hasBiometrics() async {
    try {
        return await _localAuth.canCheckBiometrics;
    } on PlatformException catch (e) {
        return false;
    }
}

//Check type of biometric auth available (Eg - Face ID, fingerprint)
Future<void> checkBiometricType() async {
  final availableBiometrics = await _localAuth.getAvailableBiometrics();
  print('Available biometrics: $availableBiometrics');
}
```

`hasBiometrics` functions checks if user device has biometric authentication by using `canCheckBiometrics`

**Authenticate user**

If hasBiometrics function returns true now we can use biometric authentication to verify user.

Call the `authenticate` method of the `LocalAuthentication` class.

The authenticate method takes 3 parameters - 

- localizedReason - is the message to show to user while prompting them for authentication. This is typically along the lines of: 'Authenticate to access MyApp.'. This must not be empty.

- authMessages - if you want to customize messages in the dialogs.

- options - for configuring further authentication related options.

```
//Authenticate using biometric
  Future<bool> authenticate() async {
    final hasBiometric = await hasBiometrics();

    if (hasBiometric) {
      return await _localAuth.authenticate(
        localizedReason: "Scan fingerprint to authenticate",
        options: const AuthenticationOptions(
          //Shows error dialog for system-related issues
          useErrorDialogs: true,
          //If true, auth dialog is show when app open from background
          stickyAuth: true,
          //Prevent non-biometric auth like such as pin, passcode.
          biometricOnly: true,
        ),
      );
    } else {
      return false;
    }
  }
```

Now let's build a simple UI for the authentication, we will have a login screen with a button and when a user is verified we will direct them to another page else we will display a snackbar of failure to authenticate.

**Home screen**
```
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(title: const Text('Home Screen')),
            body: const Center(
                child: Text('Home Screen'),
            ),
        );
    }
}
```

And in our LoginScreen return the following 

```
Scaffold(
      appBar: AppBar(title: const Text('Login Screen')),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            final isAuthenticated = await authenticate();

            if (isAuthenticated) {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (_) => HomeScreen()),
              );
            } else {
              final snackBar = SnackBar(content: Text('Auth Failed'));
              ScaffoldMessenger.of(context).showSnackBar(snackBar);
            }
          },
          child: const Text('Authenticate'),
        ),
      ),
    );
```

Now we are good to go, you can test this project in a real device to gain the experience.

Happy coding 🥳