flutter-permission-handler
flutter-permission-handler copied to clipboard
Biometrics (Face ID & Touch ID) Permission
🚀 Feature Requests
Biometrics (Touch ID + Face ID) permission in flutter_permission_handler
package
Contextualize the feature
In order to authenticate users faster, developers use different authentication solutions. One of them is Biometric Authentication. A software might have to ask for biometrics permission while the app is running. Biometrics permission in flutter_permission_handler
package could be helpful feature for developers.
Describe the feature
We could use biometrics permission as other types of permissions. For example, request popup and check status of the requested permission.
if (await Permission.biometrics.request().isGranted) { // Do something helpful }
var status = await Permission.camera.status;
Platforms affected (mark all that apply)
- ✅ :iphone: iOS
- ✅ :robot: Android
Thanks.
Hello, any updates on this? Thank you
Up this issue, please.
Any update on this? This is the only thing which is missing from this Amazing package.
Guys any update on this?
+1
+1
+1
@alisadibekov and others,
Android and iOS both do not require specific permission handling to access the biometric sensors. The only thing required is to add some configuration to the AndroidManifest.xml
or Info.plist
files informing the respective App stores that the application makes use of the biometric sensors.
For this reason there is no need to implement addition logic in the permission_handler plugin and I will close this issue. If there is something I overlooked, feel free to comment on this issue and make sure to point to the documentation for the specific platform.
Hi, @mvanbeusekom
I followed the example of local_auth package, iOS still requires user permission to use biometrics for authenticate.
Reproduce steps:
- Create flutter project
2. Change main.dart as follow
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: public_member_api_docs, avoid_print
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_auth/local_auth.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final LocalAuthentication auth = LocalAuthentication();
_SupportState _supportState = _SupportState.unknown;
bool? _canCheckBiometrics;
List<BiometricType>? _availableBiometrics;
String _authorized = 'Not Authorized';
bool _isAuthenticating = false;
@override
void initState() {
super.initState();
auth.isDeviceSupported().then(
(bool isSupported) => setState(() => _supportState = isSupported
? _SupportState.supported
: _SupportState.unsupported),
);
}
Future<void> _checkBiometrics() async {
late bool canCheckBiometrics;
try {
canCheckBiometrics = await auth.canCheckBiometrics;
} on PlatformException catch (e) {
canCheckBiometrics = false;
print(e);
}
if (!mounted) {
return;
}
setState(() {
_canCheckBiometrics = canCheckBiometrics;
});
}
Future<void> _getAvailableBiometrics() async {
late List<BiometricType> availableBiometrics;
try {
availableBiometrics = await auth.getAvailableBiometrics();
} on PlatformException catch (e) {
availableBiometrics = <BiometricType>[];
print(e);
}
if (!mounted) {
return;
}
setState(() {
_availableBiometrics = availableBiometrics;
});
}
Future<void> _authenticate() async {
bool authenticated = false;
try {
setState(() {
_isAuthenticating = true;
_authorized = 'Authenticating';
});
authenticated = await auth.authenticate(
localizedReason: 'Let OS determine authentication method',
options: const AuthenticationOptions(
stickyAuth: true,
),
);
setState(() {
_isAuthenticating = false;
});
} on PlatformException catch (e) {
print(e);
setState(() {
_isAuthenticating = false;
_authorized = 'Error - ${e.message}';
});
return;
}
if (!mounted) {
return;
}
setState(
() => _authorized = authenticated ? 'Authorized' : 'Not Authorized');
}
Future<void> _authenticateWithBiometrics() async {
bool authenticated = false;
try {
setState(() {
_isAuthenticating = true;
_authorized = 'Authenticating';
});
authenticated = await auth.authenticate(
localizedReason:
'Scan your fingerprint (or face or whatever) to authenticate',
options: const AuthenticationOptions(
stickyAuth: true,
biometricOnly: true,
),
);
setState(() {
_isAuthenticating = false;
_authorized = 'Authenticating';
});
} on PlatformException catch (e) {
print(e);
setState(() {
_isAuthenticating = false;
_authorized = 'Error - ${e.message}';
});
return;
}
if (!mounted) {
return;
}
final String message = authenticated ? 'Authorized' : 'Not Authorized';
setState(() {
_authorized = message;
});
}
Future<void> _cancelAuthentication() async {
await auth.stopAuthentication();
setState(() => _isAuthenticating = false);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: ListView(
padding: const EdgeInsets.only(top: 30),
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (_supportState == _SupportState.unknown)
const CircularProgressIndicator()
else if (_supportState == _SupportState.supported)
const Text('This device is supported')
else
const Text('This device is not supported'),
const Divider(height: 100),
Text('Can check biometrics: $_canCheckBiometrics\n'),
ElevatedButton(
onPressed: _checkBiometrics,
child: const Text('Check biometrics'),
),
const Divider(height: 100),
Text('Available biometrics: $_availableBiometrics\n'),
ElevatedButton(
onPressed: _getAvailableBiometrics,
child: const Text('Get available biometrics'),
),
const Divider(height: 100),
Text('Current State: $_authorized\n'),
if (_isAuthenticating)
ElevatedButton(
onPressed: _cancelAuthentication,
// TODO(goderbauer): Make this const when this package requires Flutter 3.8 or later.
// ignore: prefer_const_constructors
child: Row(
mainAxisSize: MainAxisSize.min,
children: const <Widget>[
Text('Cancel Authentication'),
Icon(Icons.cancel),
],
),
)
else
Column(
children: <Widget>[
ElevatedButton(
onPressed: _authenticate,
// TODO(goderbauer): Make this const when this package requires Flutter 3.8 or later.
// ignore: prefer_const_constructors
child: Row(
mainAxisSize: MainAxisSize.min,
children: const <Widget>[
Text('Authenticate'),
Icon(Icons.perm_device_information),
],
),
),
ElevatedButton(
onPressed: _authenticateWithBiometrics,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(_isAuthenticating
? 'Cancel'
: 'Authenticate: biometrics only'),
const Icon(Icons.fingerprint),
],
),
),
],
),
],
),
],
),
),
);
}
}
enum _SupportState {
unknown,
supported,
unsupported,
}
3. Add description to Info.plist
<key>NSFaceIDUsageDescription</key>
<string>This app need to scan your fingerprint or face to authenticate.</string>
Actual result:
App request user permission for the first use of biometrics.
https://github.com/Baseflow/flutter-permission-handler/assets/76845271/fdce96c6-9bb9-440c-a933-11cee7ea66f8
Flutter doctor
[✓] Flutter (Channel stable, 3.7.12, on macOS 13.4.1 22F82 darwin-x64, locale en-TH)
• Flutter version 3.7.12 on channel stable at /Users/kittipongtheothaisong/fvm/versions/3.7.12
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 4d9e56e694 (3 months ago), 2023-04-17 21:47:46 -0400
• Engine revision 1a65d409c7
• Dart version 2.19.6
• DevTools version 2.20.1
[✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
• Android SDK at /Users/kittipongtheothaisong/Library/Android/sdk
• Platform android-33, build-tools 32.1.0-rc1
• Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7590822)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 14E300c
• CocoaPods version 1.11.3
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2021.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7590822)
[✓] IntelliJ IDEA Community Edition (version 2021.3.3)
• IntelliJ at /Applications/IntelliJ IDEA CE.app
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
[✓] VS Code (version 1.79.2)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.66.0
[✓] Connected device (3 available)
• The C (mobile) • ************************* • ios • iOS 16.5.1 20F75
• macOS (desktop) • macos • darwin-x64 • macOS 13.4.1 22F82 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 114.0.5735.198
! Error: The C is busy: Fetching debug symbols for The C. Xcode will continue when The C is finished. (code -10)
[✓] HTTP Host Availability
• All required HTTP hosts are available
• No issues found!
Phone Info:
iOS version: 16.5.1 Model: iPhone 12 Pro
@MrKnos, looking into the iOS Local Authentication SDK documentation, there doesn't seem to be any explicit method to request or check permissions. This means that Apple internally will handle requesting permissions to access FaceID. This is also mentioned in this SO post:
iOS won't prompt the user for permission until the first call to
[LAContext evaluatePolicy:localizedReason:reply:]
.
As per my understanding there is no reliable way to check and request permissions.