Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
[local_auth] Avoid user confirmation on face unlock (flutter#2047)
* Define a new parameter for signaling that the transaction is sensitive.
* Up the biometric version to beta01.
* Handle no device credential error.
  • Loading branch information
mehmetf authored and Michael Klimushyn committed Sep 10, 2019
commit 94483425ae2e2dadb262cf313474b2ad209bebe8
6 changes: 6 additions & 0 deletions packages/local_auth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion packages/local_auth/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ android {

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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public AuthenticationHelper(
.setTitle((String) call.argument("signInTitle"))
.setSubtitle((String) call.argument("fingerprintHint"))
.setNegativeButtonText((String) call.argument("cancelButton"))
.setConfirmationRequired((Boolean) call.argument("sensitiveTransaction"))
.build();
}

Expand All @@ -95,13 +96,11 @@ private void stop() {
@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")) {
Expand Down
22 changes: 18 additions & 4 deletions packages/local_auth/lib/local_auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,6 +15,13 @@ enum BiometricType { face, fingerprint, iris }

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.
Expand Down Expand Up @@ -44,6 +51,11 @@ class LocalAuthentication {
/// 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
Expand All @@ -54,23 +66,25 @@ class LocalAuthentication {
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);
Expand Down
7 changes: 6 additions & 1 deletion packages/local_auth/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS device authentication sensors
such as Fingerprint Reader and Touch ID.
author: Flutter Team <[email protected]>
homepage: https://github.com/flutter/plugins/tree/master/packages/local_auth
version: 0.5.3
version: 0.6.0

flutter:
plugin:
Expand All @@ -16,6 +16,11 @@ dependencies:
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"
Expand Down
90 changes: 90 additions & 0 deletions packages/local_auth/test/local_auth_test.dart
Original file line number Diff line number Diff line change
@@ -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)),
],
);
});
});
}