Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 9448342

Browse files
mehmetfMichael Klimushyn
authored and
Michael Klimushyn
committed
[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.
1 parent 186cec7 commit 9448342

File tree

6 files changed

+127
-13
lines changed

6 files changed

+127
-13
lines changed

packages/local_auth/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.6.0
2+
3+
* Define a new parameter for signaling that the transaction is sensitive.
4+
* Up the biometric version to beta01.
5+
* Handle no device credential error.
6+
17
## 0.5.3
28

39
* Add face id detection as well by not relying on FingerprintCompat.

packages/local_auth/android/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,6 @@ android {
4848

4949
dependencies {
5050
api "androidx.core:core:1.1.0-beta01"
51-
api "androidx.biometric:biometric:1.0.0-alpha04"
51+
api "androidx.biometric:biometric:1.0.0-beta01"
5252
api "androidx.fragment:fragment:1.1.0-alpha06"
5353
}

packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public AuthenticationHelper(
7777
.setTitle((String) call.argument("signInTitle"))
7878
.setSubtitle((String) call.argument("fingerprintHint"))
7979
.setNegativeButtonText((String) call.argument("cancelButton"))
80+
.setConfirmationRequired((Boolean) call.argument("sensitiveTransaction"))
8081
.build();
8182
}
8283

@@ -95,13 +96,11 @@ private void stop() {
9596
@Override
9697
public void onAuthenticationError(int errorCode, CharSequence errString) {
9798
switch (errorCode) {
98-
// TODO(mehmetf): Re-enable when biometric alpha05 is released.
99-
// https://developer.android.com/jetpack/androidx/releases/biometric
100-
// case BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL:
101-
// completionHandler.onError(
102-
// "PasscodeNotSet",
103-
// "Phone not secured by PIN, pattern or password, or SIM is currently locked.");
104-
// break;
99+
case BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL:
100+
completionHandler.onError(
101+
"PasscodeNotSet",
102+
"Phone not secured by PIN, pattern or password, or SIM is currently locked.");
103+
break;
105104
case BiometricPrompt.ERROR_NO_SPACE:
106105
case BiometricPrompt.ERROR_NO_BIOMETRICS:
107106
if (call.argument("useErrorDialogs")) {

packages/local_auth/lib/local_auth.dart

+18-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6-
import 'dart:io';
76

87
import 'package:flutter/services.dart';
98
import 'package:meta/meta.dart';
9+
import 'package:platform/platform.dart';
1010

1111
import 'auth_strings.dart';
1212
import 'error_codes.dart';
@@ -15,6 +15,13 @@ enum BiometricType { face, fingerprint, iris }
1515

1616
const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth');
1717

18+
Platform _platform = const LocalPlatform();
19+
20+
@visibleForTesting
21+
void setMockPathProviderPlatform(Platform platform) {
22+
_platform = platform;
23+
}
24+
1825
/// A Flutter plugin for authenticating the user identity locally.
1926
class LocalAuthentication {
2027
/// Authenticates the user with biometrics available on the device.
@@ -44,6 +51,11 @@ class LocalAuthentication {
4451
/// Construct [AndroidAuthStrings] and [IOSAuthStrings] if you want to
4552
/// customize messages in the dialogs.
4653
///
54+
/// Setting [sensitiveTransaction] to true enables platform specific
55+
/// precautions. For instance, on face unlock, Android opens a confirmation
56+
/// dialog after the face is recognized to make sure the user meant to unlock
57+
/// their phone.
58+
///
4759
/// Throws an [PlatformException] if there were technical problems with local
4860
/// authentication (e.g. lack of relevant hardware). This might throw
4961
/// [PlatformException] with error code [otherOperatingSystem] on the iOS
@@ -54,23 +66,25 @@ class LocalAuthentication {
5466
bool stickyAuth = false,
5567
AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(),
5668
IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(),
69+
bool sensitiveTransaction = true,
5770
}) async {
5871
assert(localizedReason != null);
5972
final Map<String, Object> args = <String, Object>{
6073
'localizedReason': localizedReason,
6174
'useErrorDialogs': useErrorDialogs,
6275
'stickyAuth': stickyAuth,
76+
'sensitiveTransaction': sensitiveTransaction,
6377
};
64-
if (Platform.isIOS) {
78+
if (_platform.isIOS) {
6579
args.addAll(iOSAuthStrings.args);
66-
} else if (Platform.isAndroid) {
80+
} else if (_platform.isAndroid) {
6781
args.addAll(androidAuthStrings.args);
6882
} else {
6983
throw PlatformException(
7084
code: otherOperatingSystem,
7185
message: 'Local authentication does not support non-Android/iOS '
7286
'operating systems.',
73-
details: 'Your operating system is ${Platform.operatingSystem}');
87+
details: 'Your operating system is ${_platform.operatingSystem}');
7488
}
7589
return await _channel.invokeMethod<bool>(
7690
'authenticateWithBiometrics', args);

packages/local_auth/pubspec.yaml

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS device authentication sensors
33
such as Fingerprint Reader and Touch ID.
44
author: Flutter Team <[email protected]>
55
homepage: https://github.com/flutter/plugins/tree/master/packages/local_auth
6-
version: 0.5.3
6+
version: 0.6.0
77

88
flutter:
99
plugin:
@@ -16,6 +16,11 @@ dependencies:
1616
sdk: flutter
1717
meta: ^1.0.5
1818
intl: ^0.15.1
19+
platform: ^2.0.0
20+
21+
dev_dependencies:
22+
flutter_test:
23+
sdk: flutter
1924

2025
environment:
2126
sdk: ">=2.0.0-dev.28.0 <3.0.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright 2019 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:flutter/services.dart';
8+
import 'package:flutter_test/flutter_test.dart';
9+
import 'package:local_auth/auth_strings.dart';
10+
import 'package:local_auth/local_auth.dart';
11+
import 'package:platform/platform.dart';
12+
13+
void main() {
14+
TestWidgetsFlutterBinding.ensureInitialized();
15+
16+
group('LocalAuth', () {
17+
const MethodChannel channel = MethodChannel(
18+
'plugins.flutter.io/local_auth',
19+
);
20+
21+
final List<MethodCall> log = <MethodCall>[];
22+
LocalAuthentication localAuthentication;
23+
24+
setUp(() {
25+
channel.setMockMethodCallHandler((MethodCall methodCall) {
26+
log.add(methodCall);
27+
return Future<dynamic>.value(true);
28+
});
29+
localAuthentication = LocalAuthentication();
30+
log.clear();
31+
});
32+
33+
test('authenticate with no args on Android.', () async {
34+
setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android'));
35+
await localAuthentication.authenticateWithBiometrics(
36+
localizedReason: 'Needs secure');
37+
expect(
38+
log,
39+
<Matcher>[
40+
isMethodCall('authenticateWithBiometrics',
41+
arguments: <String, dynamic>{
42+
'localizedReason': 'Needs secure',
43+
'useErrorDialogs': true,
44+
'stickyAuth': false,
45+
'sensitiveTransaction': true,
46+
}..addAll(const AndroidAuthMessages().args)),
47+
],
48+
);
49+
});
50+
51+
test('authenticate with no args on iOS.', () async {
52+
setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios'));
53+
await localAuthentication.authenticateWithBiometrics(
54+
localizedReason: 'Needs secure');
55+
expect(
56+
log,
57+
<Matcher>[
58+
isMethodCall('authenticateWithBiometrics',
59+
arguments: <String, dynamic>{
60+
'localizedReason': 'Needs secure',
61+
'useErrorDialogs': true,
62+
'stickyAuth': false,
63+
'sensitiveTransaction': true,
64+
}..addAll(const IOSAuthMessages().args)),
65+
],
66+
);
67+
});
68+
69+
test('authenticate with no sensitive transaction.', () async {
70+
setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android'));
71+
await localAuthentication.authenticateWithBiometrics(
72+
localizedReason: 'Insecure',
73+
sensitiveTransaction: false,
74+
useErrorDialogs: false,
75+
);
76+
expect(
77+
log,
78+
<Matcher>[
79+
isMethodCall('authenticateWithBiometrics',
80+
arguments: <String, dynamic>{
81+
'localizedReason': 'Insecure',
82+
'useErrorDialogs': false,
83+
'stickyAuth': false,
84+
'sensitiveTransaction': false,
85+
}..addAll(const AndroidAuthMessages().args)),
86+
],
87+
);
88+
});
89+
});
90+
}

0 commit comments

Comments
 (0)