[local_auth] Add platform interface to prepare for migration to federated architecture (#4697)
diff --git a/packages/local_auth/local_auth_platform_interface/AUTHORS b/packages/local_auth/local_auth_platform_interface/AUTHORS
new file mode 100644
index 0000000..d569469
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/AUTHORS
@@ -0,0 +1,67 @@
+# Below is a list of people and organizations that have contributed
+# to the Flutter project. Names should be added to the list like so:
+#
+# Name/Organization <email address>
+
+Google Inc.
+The Chromium Authors
+German Saprykin <saprykin.h@gmail.com>
+Benjamin Sauer <sauer.benjamin@gmail.com>
+larsenthomasj@gmail.com
+Ali Bitek <alibitek@protonmail.ch>
+Pol Batlló <pol.batllo@gmail.com>
+Anatoly Pulyaevskiy
+Hayden Flinner <haydenflinner@gmail.com>
+Stefano Rodriguez <hlsroddy@gmail.com>
+Salvatore Giordano <salvatoregiordanoo@gmail.com>
+Brian Armstrong <brian@flutter.institute>
+Paul DeMarco <paulmdemarco@gmail.com>
+Fabricio Nogueira <feufeu@gmail.com>
+Simon Lightfoot <simon@devangels.london>
+Ashton Thomas <ashton@acrinta.com>
+Thomas Danner <thmsdnnr@gmail.com>
+Diego Velásquez <diego.velasquez.lopez@gmail.com>
+Hajime Nakamura <nkmrhj@gmail.com>
+Tuyển Vũ Xuân <netsoft1985@gmail.com>
+Miguel Ruivo <miguel@miguelruivo.com>
+Sarthak Verma <sarthak@artiosys.com>
+Mike Diarmid <mike@invertase.io>
+Invertase <oss@invertase.io>
+Elliot Hesp <elliot@invertase.io>
+Vince Varga <vince.varga@smaho.com>
+Aawaz Gyawali <awazgyawali@gmail.com>
+EUI Limited <ian.evans3@admiralgroup.co.uk>
+Katarina Sheremet <katarina@sheremet.ch>
+Thomas Stockx <thomas@stockxit.com>
+Sarbagya Dhaubanjar <sarbagyastha@gmail.com>
+Ozkan Eksi <ozeksi@gmail.com>
+Rishab Nayak <rishab@bu.edu>
+ko2ic <ko2ic.dev@gmail.com>
+Jonathan Younger <jonathan@daikini.com>
+Jose Sanchez <josesm82@gmail.com>
+Debkanchan Samadder <debu.samadder@gmail.com>
+Audrius Karosevicius <audrius.karosevicius@gmail.com>
+Lukasz Piliszczuk <lukasz@intheloup.io>
+SoundReply Solutions GmbH <ch@soundreply.com>
+Rafal Wachol <rwachol@gmail.com>
+Pau Picas <pau.picas@gmail.com>
+Christian Weder <chrstian.weder@yapeal.ch>
+Alexandru Tuca <salexandru.tuca@outlook.com>
+Christian Weder <chrstian.weder@yapeal.ch>
+Rhodes Davis Jr. <rody.davis.jr@gmail.com>
+Luigi Agosti <luigi@tengio.com>
+Quentin Le Guennec <quentin@tengio.com>
+Koushik Ravikumar <koushik@tengio.com>
+Nissim Dsilva <nissim@tengio.com>
+Giancarlo Rocha <giancarloiff@gmail.com>
+Ryo Miyake <ryo@miyake.id>
+Théo Champion <contact.theochampion@gmail.com>
+Kazuki Yamaguchi <y.kazuki0614n@gmail.com>
+Eitan Schwartz <eshvartz@gmail.com>
+Chris Rutkowski <chrisrutkowski89@gmail.com>
+Juan Alvarez <juan.alvarez@resideo.com>
+Aleksandr Yurkovskiy <sanekyy@gmail.com>
+Anton Borries <mail@antonborri.es>
+Alex Li <google@alexv525.com>
+Rahul Raj <64.rahulraj@gmail.com>
+Bodhi Mulders <info@bemacized.net>
diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md
new file mode 100644
index 0000000..0d8803f
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0
+
+* Initial release.
diff --git a/packages/local_auth/local_auth_platform_interface/LICENSE b/packages/local_auth/local_auth_platform_interface/LICENSE
new file mode 100644
index 0000000..c6823b8
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2013 The Flutter Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/local_auth/local_auth_platform_interface/README.md b/packages/local_auth/local_auth_platform_interface/README.md
new file mode 100644
index 0000000..3b01ced
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/README.md
@@ -0,0 +1,26 @@
+# local_auth_platform_interface
+
+A common platform interface for the [`local_auth`][1] plugin.
+
+This interface allows platform-specific implementations of the `local_auth`
+plugin, as well as the plugin itself, to ensure they are supporting the
+same interface.
+
+# Usage
+
+To implement a new platform-specific implementation of `local_auth`, extend
+[`LocalAuthPlatform`][2] with an implementation that performs the
+platform-specific behavior, and when you register your plugin, set the default
+`LocalAuthPlatform` by calling
+`LocalAuthPlatform.instance = MyLocalAuthPlatform()`.
+
+# Note on breaking changes
+
+Strongly prefer non-breaking changes (such as adding a method to the interface)
+over breaking changes for this package.
+
+See https://flutter.dev/go/platform-interface-breaking-changes for a discussion
+on why a less-clean interface is preferable to a breaking change.
+
+[1]: ../local_auth
+[2]: lib/local_auth_platform_interface.dart
diff --git a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart
new file mode 100644
index 0000000..c68a3bf
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart
@@ -0,0 +1,78 @@
+// 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.
+
+import 'package:flutter/services.dart';
+import 'package:local_auth_platform_interface/local_auth_platform_interface.dart';
+import 'package:local_auth_platform_interface/types/auth_messages.dart';
+import 'package:local_auth_platform_interface/types/auth_options.dart';
+import 'package:local_auth_platform_interface/types/biometric_type.dart';
+
+const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth');
+
+/// The default interface implementation acting as a placeholder for
+/// the native implementation to be set.
+///
+/// This implementation is not used by any of the implementations in this
+/// repository, and exists only for backward compatibility with any
+/// clients that were relying on internal details of the method channel
+/// in the pre-federated plugin.
+class DefaultLocalAuthPlatform extends LocalAuthPlatform {
+ @override
+ Future<bool> authenticate({
+ required String localizedReason,
+ required Iterable<AuthMessages> authMessages,
+ AuthenticationOptions options = const AuthenticationOptions(),
+ }) async {
+ assert(localizedReason.isNotEmpty);
+ final Map<String, Object> args = <String, Object>{
+ 'localizedReason': localizedReason,
+ 'useErrorDialogs': options.useErrorDialogs,
+ 'stickyAuth': options.stickyAuth,
+ 'sensitiveTransaction': options.sensitiveTransaction,
+ 'biometricOnly': options.biometricOnly,
+ };
+ for (final AuthMessages messages in authMessages) {
+ args.addAll(messages.args);
+ }
+ return (await _channel.invokeMethod<bool>('authenticate', args)) ?? false;
+ }
+
+ @override
+ Future<List<BiometricType>> getEnrolledBiometrics() async {
+ final List<String> result = (await _channel.invokeListMethod<String>(
+ 'getAvailableBiometrics',
+ )) ??
+ <String>[];
+ final List<BiometricType> biometrics = <BiometricType>[];
+ for (final String value in result) {
+ switch (value) {
+ case 'face':
+ biometrics.add(BiometricType.face);
+ break;
+ case 'fingerprint':
+ biometrics.add(BiometricType.fingerprint);
+ break;
+ case 'iris':
+ biometrics.add(BiometricType.iris);
+ break;
+ case 'undefined':
+ break;
+ }
+ }
+ return biometrics;
+ }
+
+ @override
+ Future<bool> deviceSupportsBiometrics() async {
+ return (await getEnrolledBiometrics()).isNotEmpty;
+ }
+
+ @override
+ Future<bool> isDeviceSupported() async =>
+ (await _channel.invokeMethod<bool>('isDeviceSupported')) ?? false;
+
+ @override
+ Future<bool> stopAuthentication() async =>
+ await _channel.invokeMethod<bool>('stopAuthentication') ?? false;
+}
diff --git a/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart
new file mode 100644
index 0000000..b909ee9
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart
@@ -0,0 +1,99 @@
+// 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.
+
+import 'package:local_auth_platform_interface/default_method_channel_platform.dart';
+import 'package:local_auth_platform_interface/types/auth_messages.dart';
+import 'package:local_auth_platform_interface/types/auth_options.dart';
+import 'package:local_auth_platform_interface/types/biometric_type.dart';
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+/// The interface that implementations of local_auth must implement.
+///
+/// Platform implementations should extend this class rather than implement it as `local_auth`
+/// does not consider newly added methods to be breaking changes. Extending this class
+/// (using `extends`) ensures that the subclass will get the default implementation, while
+/// platform implementations that `implements` this interface will be broken by newly added
+/// [LocalAuthPlatform] methods.
+abstract class LocalAuthPlatform extends PlatformInterface {
+ /// Constructs a LocalAuthPlatform.
+ LocalAuthPlatform() : super(token: _token);
+
+ static final Object _token = Object();
+
+ static LocalAuthPlatform _instance = DefaultLocalAuthPlatform();
+
+ /// The default instance of [LocalAuthPlatform] to use.
+ ///
+ /// Defaults to [DefaultLocalAuthPlatform].
+ static LocalAuthPlatform get instance => _instance;
+
+ /// Platform-specific implementations should set this with their own
+ /// platform-specific class that extends [LocalAuthPlatform] when they
+ /// register themselves.
+ static set instance(LocalAuthPlatform instance) {
+ PlatformInterface.verifyToken(instance, _token);
+ _instance = instance;
+ }
+
+ /// Authenticates the user with biometrics available on the device while also
+ /// allowing the user to use device authentication - pin, pattern, passcode.
+ ///
+ /// Returns true if the user successfully authenticated, false otherwise.
+ ///
+ /// [localizedReason] is the message to show to user while prompting them
+ /// for authentication. This is typically along the lines of: 'Please scan
+ /// your finger to access MyApp.'. This must not be empty.
+ ///
+ /// Provide [authMessages] if you want to
+ /// customize messages in the dialogs.
+ ///
+ /// Provide [options] for configuring further authentication related options.
+ ///
+ /// Throws a [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
+ /// simulator.
+ Future<bool> authenticate({
+ required String localizedReason,
+ required Iterable<AuthMessages> authMessages,
+ AuthenticationOptions options = const AuthenticationOptions(),
+ }) async {
+ throw UnimplementedError('authenticate() has not been implemented.');
+ }
+
+ /// Returns true if the device is capable of checking biometrics.
+ ///
+ /// This will return true even if there are no biometrics currently enrolled.
+ Future<bool> deviceSupportsBiometrics() async {
+ throw UnimplementedError('canCheckBiometrics() has not been implemented.');
+ }
+
+ /// Returns a list of enrolled biometrics.
+ ///
+ /// Possible values include:
+ /// - BiometricType.face
+ /// - BiometricType.fingerprint
+ /// - BiometricType.iris (not yet implemented)
+ /// - BiometricType.strong
+ /// - BiometricType.weak
+ Future<List<BiometricType>> getEnrolledBiometrics() async {
+ throw UnimplementedError(
+ 'getAvailableBiometrics() has not been implemented.');
+ }
+
+ /// Returns true if device is capable of checking biometrics or is able to
+ /// fail over to device credentials.
+ Future<bool> isDeviceSupported() async {
+ throw UnimplementedError('isDeviceSupported() has not been implemented.');
+ }
+
+ /// Cancels any authentication currently in progress.
+ ///
+ /// Returns true if auth was cancelled successfully.
+ /// Returns false if there was no authentication in progress,
+ /// or an error occurred.
+ Future<bool> stopAuthentication() async {
+ throw UnimplementedError('stopAuthentication() has not been implemented.');
+ }
+}
diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/auth_messages.dart b/packages/local_auth/local_auth_platform_interface/lib/types/auth_messages.dart
new file mode 100644
index 0000000..d51980d
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/lib/types/auth_messages.dart
@@ -0,0 +1,12 @@
+// 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.
+
+/// Abstract class for storing platform specific strings.
+abstract class AuthMessages {
+ /// Constructs an instance of [AuthMessages].
+ const AuthMessages();
+
+ /// Returns all platform-specific messages as a map.
+ Map<String, String> get args;
+}
diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart b/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart
new file mode 100644
index 0000000..c4b646c
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart
@@ -0,0 +1,60 @@
+// 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.
+
+import 'package:flutter/foundation.dart';
+
+/// Options wrapper for [LocalAuthPlatform.authenticate] parameters.
+@immutable
+class AuthenticationOptions {
+ /// Constructs a new instance.
+ const AuthenticationOptions({
+ this.useErrorDialogs = true,
+ this.stickyAuth = false,
+ this.sensitiveTransaction = true,
+ this.biometricOnly = false,
+ });
+
+ /// Whether the system will attempt to handle user-fixable issues encountered
+ /// while authenticating. For instance, if a fingerprint reader exists on the
+ /// device but there's no fingerprint registered, the plugin might attempt to
+ /// take the user to settings to add one. Anything that is not user fixable,
+ /// such as no biometric sensor on device, will still result in
+ /// a [PlatformException].
+ final bool useErrorDialogs;
+
+ /// Used when the application goes into background for any reason while the
+ /// authentication is in progress. Due to security reasons, the
+ /// authentication has to be stopped at that time. If stickyAuth is set to
+ /// true, authentication resumes when the app is resumed. If it is set to
+ /// false (default), then as soon as app is paused a failure message is sent
+ /// back to Dart and it is up to the client app to restart authentication or
+ /// do something else.
+ final bool stickyAuth;
+
+ /// Whether platform specific precautions are enabled. For instance, on face
+ /// unlock, Android opens a confirmation dialog after the face is recognized
+ /// to make sure the user meant to unlock their device.
+ final bool sensitiveTransaction;
+
+ /// Prevent authentications from using non-biometric local authentication
+ /// such as pin, passcode, or pattern.
+ final bool biometricOnly;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is AuthenticationOptions &&
+ runtimeType == other.runtimeType &&
+ useErrorDialogs == other.useErrorDialogs &&
+ stickyAuth == other.stickyAuth &&
+ sensitiveTransaction == other.sensitiveTransaction &&
+ biometricOnly == other.biometricOnly;
+
+ @override
+ int get hashCode =>
+ useErrorDialogs.hashCode ^
+ stickyAuth.hashCode ^
+ sensitiveTransaction.hashCode ^
+ biometricOnly.hashCode;
+}
diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/biometric_type.dart b/packages/local_auth/local_auth_platform_interface/lib/types/biometric_type.dart
new file mode 100644
index 0000000..9c335e2
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/lib/types/biometric_type.dart
@@ -0,0 +1,27 @@
+// 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.
+
+/// Various types of biometric authentication.
+/// Some platforms report specific biometric types, while others report only
+/// classifications like strong and weak.
+enum BiometricType {
+ /// Face authentication.
+ face,
+
+ /// Fingerprint authentication.
+ fingerprint,
+
+ /// Iris authentication.
+ iris,
+
+ /// Any biometric (e.g. fingerprint, iris, or face) on the device that the
+ /// platform API considers to be strong. For example, on Android this
+ /// corresponds to Class 3.
+ strong,
+
+ /// Any biometric (e.g. fingerprint, iris, or face) on the device that the
+ /// platform API considers to be weak. For example, on Android this
+ /// corresponds to Class 2.
+ weak,
+}
diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml
new file mode 100644
index 0000000..f042689
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml
@@ -0,0 +1,22 @@
+name: local_auth_platform_interface
+description: A common platform interface for the local_auth plugin.
+repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_platform_interface
+issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22
+# NOTE: We strongly prefer non-breaking changes, even at the expense of a
+# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
+version: 1.0.0
+
+environment:
+ sdk: ">=2.14.0 <3.0.0"
+ flutter: ">=2.8.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ intl: ^0.17.0
+ plugin_platform_interface: ^2.1.2
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ mockito: ^5.0.0
\ No newline at end of file
diff --git a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart
new file mode 100644
index 0000000..3853fd8
--- /dev/null
+++ b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart
@@ -0,0 +1,180 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:local_auth_platform_interface/default_method_channel_platform.dart';
+import 'package:local_auth_platform_interface/local_auth_platform_interface.dart';
+import 'package:local_auth_platform_interface/types/auth_messages.dart';
+import 'package:local_auth_platform_interface/types/auth_options.dart';
+import 'package:local_auth_platform_interface/types/biometric_type.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ const MethodChannel channel = MethodChannel(
+ 'plugins.flutter.io/local_auth',
+ );
+
+ final List<MethodCall> log = <MethodCall>[];
+ late LocalAuthPlatform localAuthentication;
+
+ test(
+ 'DefaultLocalAuthPlatform is registered as the default platform implementation',
+ () async {
+ expect(LocalAuthPlatform.instance,
+ const TypeMatcher<DefaultLocalAuthPlatform>());
+ });
+
+ test('getAvailableBiometrics', () async {
+ channel.setMockMethodCallHandler((MethodCall methodCall) {
+ log.add(methodCall);
+ return Future<dynamic>.value(<BiometricType>[]);
+ });
+ localAuthentication = DefaultLocalAuthPlatform();
+ log.clear();
+ await localAuthentication.getEnrolledBiometrics();
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('getAvailableBiometrics', arguments: null),
+ ],
+ );
+ });
+
+ group('Boolean returning methods', () {
+ setUp(() {
+ channel.setMockMethodCallHandler((MethodCall methodCall) {
+ log.add(methodCall);
+ return Future<dynamic>.value(true);
+ });
+ localAuthentication = DefaultLocalAuthPlatform();
+ log.clear();
+ });
+
+ test('isDeviceSupported', () async {
+ await localAuthentication.isDeviceSupported();
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('isDeviceSupported', arguments: null),
+ ],
+ );
+ });
+
+ test('stopAuthentication', () async {
+ await localAuthentication.stopAuthentication();
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('stopAuthentication', arguments: null),
+ ],
+ );
+ });
+
+ group('authenticate with device auth fail over', () {
+ test('authenticate with no args.', () async {
+ await localAuthentication.authenticate(
+ authMessages: <AuthMessages>[],
+ localizedReason: 'Needs secure',
+ options: const AuthenticationOptions(biometricOnly: true),
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall(
+ 'authenticate',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Needs secure',
+ 'useErrorDialogs': true,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': true,
+ 'biometricOnly': true,
+ },
+ ),
+ ],
+ );
+ });
+
+ test('authenticate with no sensitive transaction.', () async {
+ await localAuthentication.authenticate(
+ authMessages: <AuthMessages>[],
+ localizedReason: 'Insecure',
+ options: const AuthenticationOptions(
+ sensitiveTransaction: false,
+ useErrorDialogs: false,
+ biometricOnly: true,
+ ),
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall(
+ 'authenticate',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Insecure',
+ 'useErrorDialogs': false,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': false,
+ 'biometricOnly': true,
+ },
+ ),
+ ],
+ );
+ });
+ });
+
+ group('authenticate with biometrics only', () {
+ test('authenticate with no args.', () async {
+ await localAuthentication.authenticate(
+ authMessages: <AuthMessages>[],
+ localizedReason: 'Needs secure',
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall(
+ 'authenticate',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Needs secure',
+ 'useErrorDialogs': true,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': true,
+ 'biometricOnly': false,
+ },
+ ),
+ ],
+ );
+ });
+
+ test('authenticate with no sensitive transaction.', () async {
+ await localAuthentication.authenticate(
+ authMessages: <AuthMessages>[],
+ localizedReason: 'Insecure',
+ options: const AuthenticationOptions(
+ sensitiveTransaction: false,
+ useErrorDialogs: false,
+ ),
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall(
+ 'authenticate',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Insecure',
+ 'useErrorDialogs': false,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': false,
+ 'biometricOnly': false,
+ },
+ ),
+ ],
+ );
+ });
+ });
+ });
+}