Biometric authentication provides a secure and user-friendly way to authenticate users in mobile applications. Flutter, Google’s UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase, makes implementing biometric authentication straightforward. This guide will walk you through the steps to add biometric authentication to your Flutter app.
What is Biometric Authentication?
Biometric authentication uses unique biological traits to verify a user’s identity. Common biometric methods include fingerprint scanning, facial recognition, and iris scanning. Integrating biometric authentication into your app enhances security while providing a seamless user experience.
Why Use Biometric Authentication?
- Enhanced Security: Biometrics are difficult to replicate, providing a higher level of security compared to traditional passwords or PINs.
- User Convenience: Biometric authentication is typically faster and easier than typing in passwords or PINs.
- Improved User Experience: Streamlines the login process, reducing friction for users.
Prerequisites
Before you start, ensure you have the following:
- Flutter SDK installed and configured.
- An IDE (e.g., VS Code, Android Studio).
- A physical device (biometric authentication may not work reliably on emulators).
Step-by-Step Implementation
Step 1: Add the local_auth
Package
The local_auth
package is the official Flutter plugin for handling local authentication, including biometric authentication. Add it to your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
local_auth: ^2.0.0
Run flutter pub get
to install the package.
Step 2: Configure Native Platforms
Depending on the target platforms (Android, iOS), you need to configure the native settings.
Android Configuration
Add the following permission to your AndroidManifest.xml
file (android/app/src/main/AndroidManifest.xml
):
If you are targeting Android SDK 28 and below, use USE_FINGERPRINT
instead:
Also, ensure your minSdkVersion
is set to at least 23 in your android/app/build.gradle
file:
android {
defaultConfig {
minSdkVersion 23
}
}
iOS Configuration
Add the following key-value pair to your Info.plist
file (ios/Runner/Info.plist
):
NSFaceIDUsageDescription
Why is my app authenticating using face ID?
Replace the string with a description of why your app needs to use Face ID or Touch ID. Apple requires this to protect user privacy.
Step 3: Implement Biometric Authentication
Now, let’s implement the biometric authentication logic in your Flutter app.
import 'package:flutter/material.dart';
import 'package:local_auth/local_auth.dart';
import 'package:flutter/services.dart';
class BiometricAuthScreen extends StatefulWidget {
@override
_BiometricAuthScreenState createState() => _BiometricAuthScreenState();
}
class _BiometricAuthScreenState extends State {
final LocalAuthentication auth = LocalAuthentication();
bool _canCheckBiometrics = false;
List _availableBiometrics = [];
String _authorized = 'Not Authorized';
@override
void initState() {
super.initState();
_checkBiometrics();
_getAvailableBiometrics();
}
Future _checkBiometrics() async {
bool canCheck = false;
try {
canCheck = await auth.canCheckBiometrics;
} on PlatformException catch (e) {
print(e);
}
if (!mounted) return;
setState(() {
_canCheckBiometrics = canCheck;
});
}
Future _getAvailableBiometrics() async {
List availableBiometrics = [];
try {
availableBiometrics = await auth.getAvailableBiometrics();
} on PlatformException catch (e) {
print(e);
}
if (!mounted) return;
setState(() {
_availableBiometrics = availableBiometrics;
});
}
Future _authenticate() async {
bool authenticated = false;
try {
authenticated = await auth.authenticate(
localizedReason: 'Let OS determine authentication method',
options: const AuthenticationOptions(
stickyAuth: true,
biometricOnly: true
),
);
print("Authenticated : $authenticated");
} on PlatformException catch (e) {
print(e);
setState(() {
_authorized = "Error - ${e.message}";
});
return;
}
if (!mounted) return;
setState(() {
_authorized = authenticated ? 'Authorized' : 'Not Authorized';
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Biometric Authentication'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Can check biometrics: $_canCheckBiometrics\n'),
Text('Available biometrics: $_availableBiometrics\n'),
Text('Current State: $_authorized\n'),
ElevatedButton(
onPressed: _authenticate,
child: Text('Authenticate'),
),
],
),
),
);
}
}
void main() {
runApp(MaterialApp(
home: BiometricAuthScreen(),
));
}
Explanation:
LocalAuthentication
: Initializes theLocalAuthentication
object._checkBiometrics()
: Checks if biometric authentication is available on the device._getAvailableBiometrics()
: Retrieves a list of available biometric types (e.g., fingerprint, face)._authenticate()
: Triggers the biometric authentication process with a localized reason (the message displayed to the user).BiometricAuthScreen
: A StatefulWidget that displays the status and a button to trigger authentication.
Handling Authentication Failures
You should also handle potential errors during the authentication process. Add error handling within the _authenticate()
method:
Future _authenticate() async {
try {
final bool didAuthenticate = await auth.authenticate(
localizedReason: 'Authenticate to proceed',
options: const AuthenticationOptions(
stickyAuth: true,
biometricOnly: true,
));
setState(() {
_authorized =
didAuthenticate ? 'Authorized' : 'Not Authorized';
});
} on PlatformException catch (e) {
if (e.code == auth_error.notAvailable) {
// Handle the case where the authentication is not available on the device
setState(() {
_authorized = 'Biometric authentication not available';
});
} else if (e.code == auth_error.lockedOut || e.code == auth_error.permanentlyLockedOut) {
// Handle the case where the user is locked out
setState(() {
_authorized = 'Biometric authentication locked out';
});
} else {
// Handle other errors
setState(() {
_authorized = 'Error: ${e.message}';
});
}
}
}
Best Practices
- Provide Fallback: Always provide an alternative authentication method (e.g., password, PIN) in case biometric authentication fails or is not available.
- Informative UI: Provide clear and informative UI messages to guide the user through the authentication process.
- Security Considerations: Be aware of security considerations, such as data encryption and secure storage of biometric data.
- Test on Multiple Devices: Test your implementation on various devices and platforms to ensure compatibility and reliability.
Conclusion
Implementing biometric authentication in your Flutter application is straightforward with the local_auth
package. By following these steps and best practices, you can enhance the security and user experience of your app, providing a seamless and secure way for users to authenticate. Always remember to handle errors gracefully and provide alternative authentication methods for a comprehensive solution.