[local_auth] Avoid user confirmation on face unlock (#2047)
* Define a new parameter for signaling that the transaction is sensitive.
* Up the biometric version to beta01.
* Handle no device credential error.
diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md
index 1988028..832efc7 100644
--- a/packages/local_auth/CHANGELOG.md
+++ b/packages/local_auth/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.6.0
+
+* Define a new parameter for signaling that the transaction is sensitive.
+* Up the biometric version to beta01.
+* Handle no device credential error.
+
## 0.5.3
* Add face id detection as well by not relying on FingerprintCompat.
diff --git a/packages/local_auth/android/build.gradle b/packages/local_auth/android/build.gradle
index 142b606..74fe6c5 100644
--- a/packages/local_auth/android/build.gradle
+++ b/packages/local_auth/android/build.gradle
@@ -48,6 +48,6 @@
dependencies {
api "androidx.core:core:1.1.0-beta01"
- api "androidx.biometric:biometric:1.0.0-alpha04"
+ api "androidx.biometric:biometric:1.0.0-beta01"
api "androidx.fragment:fragment:1.1.0-alpha06"
}
diff --git a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
index 46d7bf3..a4ef01b 100644
--- a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
+++ b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
@@ -77,6 +77,7 @@
.setTitle((String) call.argument("signInTitle"))
.setSubtitle((String) call.argument("fingerprintHint"))
.setNegativeButtonText((String) call.argument("cancelButton"))
+ .setConfirmationRequired((Boolean) call.argument("sensitiveTransaction"))
.build();
}
@@ -95,13 +96,11 @@
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
switch (errorCode) {
- // TODO(mehmetf): Re-enable when biometric alpha05 is released.
- // https://developer.android.com/jetpack/androidx/releases/biometric
- // case BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL:
- // completionHandler.onError(
- // "PasscodeNotSet",
- // "Phone not secured by PIN, pattern or password, or SIM is currently locked.");
- // break;
+ case BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL:
+ completionHandler.onError(
+ "PasscodeNotSet",
+ "Phone not secured by PIN, pattern or password, or SIM is currently locked.");
+ break;
case BiometricPrompt.ERROR_NO_SPACE:
case BiometricPrompt.ERROR_NO_BIOMETRICS:
if (call.argument("useErrorDialogs")) {
diff --git a/packages/local_auth/lib/local_auth.dart b/packages/local_auth/lib/local_auth.dart
index d5f2ac7..df69a12 100644
--- a/packages/local_auth/lib/local_auth.dart
+++ b/packages/local_auth/lib/local_auth.dart
@@ -3,10 +3,10 @@
// found in the LICENSE file.
import 'dart:async';
-import 'dart:io';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
import 'auth_strings.dart';
import 'error_codes.dart';
@@ -15,6 +15,13 @@
const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth');
+Platform _platform = const LocalPlatform();
+
+@visibleForTesting
+void setMockPathProviderPlatform(Platform platform) {
+ _platform = platform;
+}
+
/// A Flutter plugin for authenticating the user identity locally.
class LocalAuthentication {
/// Authenticates the user with biometrics available on the device.
@@ -44,6 +51,11 @@
/// Construct [AndroidAuthStrings] and [IOSAuthStrings] if you want to
/// customize messages in the dialogs.
///
+ /// Setting [sensitiveTransaction] to true enables platform specific
+ /// precautions. For instance, on face unlock, Android opens a confirmation
+ /// dialog after the face is recognized to make sure the user meant to unlock
+ /// their phone.
+ ///
/// Throws an [PlatformException] if there were technical problems with local
/// authentication (e.g. lack of relevant hardware). This might throw
/// [PlatformException] with error code [otherOperatingSystem] on the iOS
@@ -54,23 +66,25 @@
bool stickyAuth = false,
AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(),
IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(),
+ bool sensitiveTransaction = true,
}) async {
assert(localizedReason != null);
final Map<String, Object> args = <String, Object>{
'localizedReason': localizedReason,
'useErrorDialogs': useErrorDialogs,
'stickyAuth': stickyAuth,
+ 'sensitiveTransaction': sensitiveTransaction,
};
- if (Platform.isIOS) {
+ if (_platform.isIOS) {
args.addAll(iOSAuthStrings.args);
- } else if (Platform.isAndroid) {
+ } else if (_platform.isAndroid) {
args.addAll(androidAuthStrings.args);
} else {
throw PlatformException(
code: otherOperatingSystem,
message: 'Local authentication does not support non-Android/iOS '
'operating systems.',
- details: 'Your operating system is ${Platform.operatingSystem}');
+ details: 'Your operating system is ${_platform.operatingSystem}');
}
return await _channel.invokeMethod<bool>(
'authenticateWithBiometrics', args);
diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml
index 286d7aa..a78f628 100644
--- a/packages/local_auth/pubspec.yaml
+++ b/packages/local_auth/pubspec.yaml
@@ -3,7 +3,7 @@
such as Fingerprint Reader and Touch ID.
author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/local_auth
-version: 0.5.3
+version: 0.6.0
flutter:
plugin:
@@ -16,6 +16,11 @@
sdk: flutter
meta: ^1.0.5
intl: ^0.15.1
+ platform: ^2.0.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
environment:
sdk: ">=2.0.0-dev.28.0 <3.0.0"
diff --git a/packages/local_auth/test/local_auth_test.dart b/packages/local_auth/test/local_auth_test.dart
new file mode 100644
index 0000000..205c5f7
--- /dev/null
+++ b/packages/local_auth/test/local_auth_test.dart
@@ -0,0 +1,90 @@
+// Copyright 2019 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.
+
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:local_auth/auth_strings.dart';
+import 'package:local_auth/local_auth.dart';
+import 'package:platform/platform.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ group('LocalAuth', () {
+ const MethodChannel channel = MethodChannel(
+ 'plugins.flutter.io/local_auth',
+ );
+
+ final List<MethodCall> log = <MethodCall>[];
+ LocalAuthentication localAuthentication;
+
+ setUp(() {
+ channel.setMockMethodCallHandler((MethodCall methodCall) {
+ log.add(methodCall);
+ return Future<dynamic>.value(true);
+ });
+ localAuthentication = LocalAuthentication();
+ log.clear();
+ });
+
+ test('authenticate with no args on Android.', () async {
+ setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android'));
+ await localAuthentication.authenticateWithBiometrics(
+ localizedReason: 'Needs secure');
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('authenticateWithBiometrics',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Needs secure',
+ 'useErrorDialogs': true,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': true,
+ }..addAll(const AndroidAuthMessages().args)),
+ ],
+ );
+ });
+
+ test('authenticate with no args on iOS.', () async {
+ setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios'));
+ await localAuthentication.authenticateWithBiometrics(
+ localizedReason: 'Needs secure');
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('authenticateWithBiometrics',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Needs secure',
+ 'useErrorDialogs': true,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': true,
+ }..addAll(const IOSAuthMessages().args)),
+ ],
+ );
+ });
+
+ test('authenticate with no sensitive transaction.', () async {
+ setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android'));
+ await localAuthentication.authenticateWithBiometrics(
+ localizedReason: 'Insecure',
+ sensitiveTransaction: false,
+ useErrorDialogs: false,
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('authenticateWithBiometrics',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Insecure',
+ 'useErrorDialogs': false,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': false,
+ }..addAll(const AndroidAuthMessages().args)),
+ ],
+ );
+ });
+ });
+}