diff --git a/packages/local_auth/local_auth/AUTHORS b/packages/local_auth/local_auth/AUTHORS index 493a0b4ef9c2..d5694690c247 100644 --- a/packages/local_auth/local_auth/AUTHORS +++ b/packages/local_auth/local_auth/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Bodhi Mulders diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 1ca9fe146c7b..deac871d935f 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,7 +1,42 @@ -## NEXT +## 2.0.0 +* Migrates plugin to federated architecture. * Adds OS version support information to README. -* Switches over to default method implementation in new platform interface. +* BREAKING CHANGE: Deprecated method `authenticateWithBiometrics` has been removed. + Use `authenticate` instead. +* BREAKING CHANGE: Enum `BiometricType` has been expanded with options for `strong` and `weak`, + and applications should be updated to handle these accordingly. +* BREAKING CHANGE: Parameters of `authenticate` have been changed. + + Example: + ```dart + // Old way of calling `authenticate`. + Future authenticate( + localizedReason: 'localized reason', + useErrorDialogs: true, + stickyAuth: false, + androidAuthStrings: const AndroidAuthMessages(), + iOSAuthStrings: const IOSAuthMessages(), + sensitiveTransaction: true, + biometricOnly: false, + ); + // New way of calling `authenticate`. + Future authenticate( + localizedReason: 'localized reason', + authMessages: const [ + IOSAuthMessages(), + AndroidAuthMessages() + ], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: false, + sensitiveTransaction: true, + biometricOnly: false, + ), + ); + ``` + + ## 1.1.11 diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index a9604b3411b7..92ad7cf4fb3f 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -79,9 +79,12 @@ class _MyAppState extends State { _authorized = 'Authenticating'; }); authenticated = await auth.authenticate( - localizedReason: 'Let OS determine authentication method', + localizedReason: 'Let OS determine authentication method', + options: const AuthenticationOptions( useErrorDialogs: true, - stickyAuth: true); + stickyAuth: true, + ), + ); setState(() { _isAuthenticating = false; }); @@ -109,11 +112,14 @@ class _MyAppState extends State { _authorized = 'Authenticating'; }); authenticated = await auth.authenticate( - localizedReason: - 'Scan your fingerprint (or face or whatever) to authenticate', + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + options: const AuthenticationOptions( useErrorDialogs: true, stickyAuth: true, - biometricOnly: true); + biometricOnly: true, + ), + ); setState(() { _isAuthenticating = false; _authorized = 'Authenticating'; diff --git a/packages/local_auth/local_auth/lib/auth_strings.dart b/packages/local_auth/local_auth/lib/auth_strings.dart deleted file mode 100644 index 585742ac68c2..000000000000 --- a/packages/local_auth/local_auth/lib/auth_strings.dart +++ /dev/null @@ -1,164 +0,0 @@ -// 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. - -// This is a temporary ignore to allow us to land a new set of linter rules in a -// series of manageable patches instead of one gigantic PR. It disables some of -// the new lints that are already failing on this plugin, for this plugin. It -// should be deleted and the failing lints addressed as soon as possible. -// ignore_for_file: public_member_api_docs - -import 'package:intl/intl.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; - -/// Android side authentication messages. -/// -/// Provides default values for all messages. -class AndroidAuthMessages extends AuthMessages { - const AndroidAuthMessages({ - this.biometricHint, - this.biometricNotRecognized, - this.biometricRequiredTitle, - this.biometricSuccess, - this.cancelButton, - this.deviceCredentialsRequiredTitle, - this.deviceCredentialsSetupDescription, - this.goToSettingsButton, - this.goToSettingsDescription, - this.signInTitle, - }); - - final String? biometricHint; - final String? biometricNotRecognized; - final String? biometricRequiredTitle; - final String? biometricSuccess; - final String? cancelButton; - final String? deviceCredentialsRequiredTitle; - final String? deviceCredentialsSetupDescription; - final String? goToSettingsButton; - final String? goToSettingsDescription; - final String? signInTitle; - - @override - Map get args { - return { - 'biometricHint': biometricHint ?? androidBiometricHint, - 'biometricNotRecognized': - biometricNotRecognized ?? androidBiometricNotRecognized, - 'biometricSuccess': biometricSuccess ?? androidBiometricSuccess, - 'biometricRequired': - biometricRequiredTitle ?? androidBiometricRequiredTitle, - 'cancelButton': cancelButton ?? androidCancelButton, - 'deviceCredentialsRequired': deviceCredentialsRequiredTitle ?? - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': deviceCredentialsSetupDescription ?? - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettingsButton ?? goToSettings, - 'goToSettingDescription': - goToSettingsDescription ?? androidGoToSettingsDescription, - 'signInTitle': signInTitle ?? androidSignInTitle, - }; - } -} - -/// iOS side authentication messages. -/// -/// Provides default values for all messages. -class IOSAuthMessages extends AuthMessages { - const IOSAuthMessages({ - this.lockOut, - this.goToSettingsButton, - this.goToSettingsDescription, - this.cancelButton, - this.localizedFallbackTitle, - }); - - final String? lockOut; - final String? goToSettingsButton; - final String? goToSettingsDescription; - final String? cancelButton; - final String? localizedFallbackTitle; - - @override - Map get args { - return { - 'lockOut': lockOut ?? iOSLockOut, - 'goToSetting': goToSettingsButton ?? goToSettings, - 'goToSettingDescriptionIOS': - goToSettingsDescription ?? iOSGoToSettingsDescription, - 'okButton': cancelButton ?? iOSOkButton, - if (localizedFallbackTitle != null) - 'localizedFallbackTitle': localizedFallbackTitle!, - }; - } -} - -// Strings for local_authentication plugin. Currently supports English. -// Intl.message must be string literals. -String get androidBiometricHint => Intl.message('Verify identity', - desc: - 'Hint message advising the user how to authenticate with biometrics. It is ' - 'used on Android side. Maximum 60 characters.'); - -String get androidBiometricNotRecognized => - Intl.message('Not recognized. Try again.', - desc: 'Message to let the user know that authentication was failed. It ' - 'is used on Android side. Maximum 60 characters.'); - -String get androidBiometricSuccess => Intl.message('Success', - desc: 'Message to let the user know that authentication was successful. It ' - 'is used on Android side. Maximum 60 characters.'); - -String get androidCancelButton => Intl.message('Cancel', - desc: 'Message showed on a button that the user can click to leave the ' - 'current dialog. It is used on Android side. Maximum 30 characters.'); - -String get androidSignInTitle => Intl.message('Authentication required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'that they need to scan biometric to continue. It is used on ' - 'Android side. Maximum 60 characters.'); - -String get androidBiometricRequiredTitle => Intl.message('Biometric required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'has not set up biometric authentication on their device. It is used on Android' - ' side. Maximum 60 characters.'); - -String get androidDeviceCredentialsRequiredTitle => Intl.message( - 'Device credentials required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'has not set up credentials authentication on their device. It is used on Android' - ' side. Maximum 60 characters.'); - -String get androidDeviceCredentialsSetupDescription => Intl.message( - 'Device credentials required', - desc: 'Message advising the user to go to the settings and configure ' - 'device credentials on their device. It shows in a dialog on Android side.'); - -String get goToSettings => Intl.message('Go to settings', - desc: 'Message showed on a button that the user can click to go to ' - 'settings pages from the current dialog. It is used on both Android ' - 'and iOS side. Maximum 30 characters.'); - -String get androidGoToSettingsDescription => Intl.message( - 'Biometric authentication is not set up on your device. Go to ' - '\'Settings > Security\' to add biometric authentication.', - desc: 'Message advising the user to go to the settings and configure ' - 'biometric on their device. It shows in a dialog on Android side.'); - -String get iOSLockOut => Intl.message( - 'Biometric authentication is disabled. Please lock and unlock your screen to ' - 'enable it.', - desc: - 'Message advising the user to re-enable biometrics on their device. It ' - 'shows in a dialog on iOS side.'); - -String get iOSGoToSettingsDescription => Intl.message( - 'Biometric authentication is not set up on your device. Please either enable ' - 'Touch ID or Face ID on your phone.', - desc: - 'Message advising the user to go to the settings and configure Biometrics ' - 'for their device. It shows in a dialog on iOS side.'); - -String get iOSOkButton => Intl.message('OK', - desc: 'Message showed on a button that the user can click to leave the ' - 'current dialog. It is used on iOS side. Maximum 30 characters.'); diff --git a/packages/local_auth/local_auth/lib/local_auth.dart b/packages/local_auth/local_auth/lib/local_auth.dart index 32818b31783d..7c42fedc7755 100644 --- a/packages/local_auth/local_auth/lib/local_auth.dart +++ b/packages/local_auth/local_auth/lib/local_auth.dart @@ -2,144 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This is a temporary ignore to allow us to land a new set of linter rules in a -// series of manageable patches instead of one gigantic PR. It disables some of -// the new lints that are already failing on this plugin, for this plugin. It -// should be deleted and the failing lints addressed as soon as possible. -// ignore_for_file: public_member_api_docs - -import 'dart:async'; - -import 'package:flutter/foundation.dart' show visibleForTesting; -import 'package:flutter/services.dart'; -import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:platform/platform.dart'; -import 'auth_strings.dart'; - -export 'package:local_auth_platform_interface/types/biometric_type.dart'; - -Platform _platform = const LocalPlatform(); - -@visibleForTesting -void setMockPathProviderPlatform(Platform platform) { - _platform = platform; -} - -/// A Flutter plugin for authenticating the user identity locally. -class LocalAuthentication { - /// The `authenticateWithBiometrics` method has been deprecated. - /// Use `authenticate` with `biometricOnly: true` instead. - @Deprecated('Use `authenticate` with `biometricOnly: true` instead') - Future authenticateWithBiometrics({ - required String localizedReason, - bool useErrorDialogs = true, - bool stickyAuth = false, - AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(), - IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), - bool sensitiveTransaction = true, - }) => - LocalAuthPlatform.instance.authenticate( - localizedReason: localizedReason, - authMessages: [iOSAuthStrings, androidAuthStrings], - options: AuthenticationOptions( - useErrorDialogs: useErrorDialogs, - stickyAuth: stickyAuth, - sensitiveTransaction: sensitiveTransaction, - biometricOnly: true, - ), - ); - - /// 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. - /// - /// [useErrorDialogs] = true means the system will attempt to handle user - /// fixable issues encountered while authenticating. For instance, if - /// fingerprint reader exists on the phone but there's no fingerprint - /// registered, the plugin will attempt to take the user to settings to add - /// one. Anything that is not user fixable, such as no biometric sensor on - /// device, will be returned as a [PlatformException]. - /// - /// [stickyAuth] is 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. - /// - /// 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. - /// - /// Setting [biometricOnly] to true prevents authenticates from using non-biometric - /// local authentication such as pin, passcode, and passcode. - /// - /// 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 - /// simulator. - Future authenticate({ - required String localizedReason, - bool useErrorDialogs = true, - bool stickyAuth = false, - AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(), - IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), - bool sensitiveTransaction = true, - bool biometricOnly = false, - }) { - return LocalAuthPlatform.instance.authenticate( - localizedReason: localizedReason, - authMessages: [iOSAuthStrings, androidAuthStrings], - options: AuthenticationOptions( - useErrorDialogs: useErrorDialogs, - stickyAuth: stickyAuth, - sensitiveTransaction: sensitiveTransaction, - biometricOnly: biometricOnly, - ), - ); - } - - /// Returns true if auth was cancelled successfully. - /// This api only works for Android. - /// Returns false if there was some error or no auth in progress. - /// - /// Returns [Future] bool true or false: - Future stopAuthentication() async { - if (_platform.isAndroid) { - return LocalAuthPlatform.instance.stopAuthentication(); - } - return true; - } - - /// Returns true if device is capable of checking biometrics - /// - /// Returns a [Future] bool true or false: - Future get canCheckBiometrics => - LocalAuthPlatform.instance.deviceSupportsBiometrics(); - - /// Returns true if device is capable of checking biometrics or is able to - /// fail over to device credentials. - /// - /// Returns a [Future] bool true or false: - Future isDeviceSupported() async => - LocalAuthPlatform.instance.isDeviceSupported(); - - /// Returns a list of enrolled biometrics - /// - /// Returns a [Future] List with the following possibilities: - /// - BiometricType.face - /// - BiometricType.fingerprint - /// - BiometricType.iris (not yet implemented) - Future> getAvailableBiometrics() => - LocalAuthPlatform.instance.getEnrolledBiometrics(); -} +export 'package:local_auth/src/local_auth.dart' show LocalAuthentication; +export 'package:local_auth_platform_interface/types/auth_options.dart' + show AuthenticationOptions; +export 'package:local_auth_platform_interface/types/biometric_type.dart' + show BiometricType; diff --git a/packages/local_auth/local_auth/lib/src/local_auth.dart b/packages/local_auth/local_auth/lib/src/local_auth.dart new file mode 100644 index 000000000000..508e2b14e129 --- /dev/null +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -0,0 +1,77 @@ +// 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. + +// This is a temporary ignore to allow us to land a new set of linter rules in a +// series of manageable patches instead of one gigantic PR. It disables some of +// the new lints that are already failing on this plugin, for this plugin. It +// should be deleted and the failing lints addressed as soon as possible. +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:local_auth/src/types/error_codes.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_ios/local_auth_ios.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/biometric_type.dart'; + +/// A Flutter plugin for authenticating the user identity locally. +class LocalAuthentication { + /// 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: 'Authenticate + /// 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 authenticate( + {required String localizedReason, + Iterable authMessages = const [ + IOSAuthMessages(), + AndroidAuthMessages() + ], + AuthenticationOptions options = const AuthenticationOptions()}) { + return LocalAuthPlatform.instance.authenticate( + localizedReason: localizedReason, + authMessages: authMessages, + options: options, + ); + } + + /// Cancels any in-progress authentication, returning true if auth was + /// cancelled successfully. + /// + /// This API is not supported by all platforms. + /// Returns false if there was some error, no authentication in progress, + /// or the current platform lacks support. + Future stopAuthentication() async { + return LocalAuthPlatform.instance.stopAuthentication(); + } + + /// Returns true if device is capable of checking biometrics. + Future get canCheckBiometrics => + LocalAuthPlatform.instance.deviceSupportsBiometrics(); + + /// Returns true if device is capable of checking biometrics or is able to + /// fail over to device credentials. + Future isDeviceSupported() async => + LocalAuthPlatform.instance.isDeviceSupported(); + + /// Returns a list of enrolled biometrics. + Future> getAvailableBiometrics() => + LocalAuthPlatform.instance.getEnrolledBiometrics(); +} diff --git a/packages/local_auth/local_auth/lib/error_codes.dart b/packages/local_auth/local_auth/lib/src/types/error_codes.dart similarity index 93% rename from packages/local_auth/local_auth/lib/error_codes.dart rename to packages/local_auth/local_auth/lib/src/types/error_codes.dart index bcf15b7b2154..3426099bacbd 100644 --- a/packages/local_auth/local_auth/lib/error_codes.dart +++ b/packages/local_auth/local_auth/lib/src/types/error_codes.dart @@ -15,7 +15,7 @@ const String notEnrolled = 'NotEnrolled'; /// Indicates the device does not have a Touch ID/fingerprint scanner. const String notAvailable = 'NotAvailable'; -/// Indicates the device operating system is not iOS or Android. +/// Indicates the device operating system is unsupported. const String otherOperatingSystem = 'OtherOperatingSystem'; /// Indicates the API lock out due to too many attempts. diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index baf5d46b7b00..fa055fab17f8 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,11 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.1.11 - -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 2.0.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,15 +20,10 @@ flutter: dependencies: flutter: sdk: flutter - flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 -# Temporary path dependencies to allow moving Android and iOS implementations. - local_auth_android: - path: ../local_auth_android - local_auth_ios: - path: ../local_auth_ios + local_auth_android: ^1.0.0 + local_auth_ios: ^1.0.1 local_auth_platform_interface: ^1.0.1 - platform: ^3.0.0 dev_dependencies: flutter_driver: @@ -42,3 +33,4 @@ dev_dependencies: integration_test: sdk: flutter mockito: ^5.1.0 + plugin_platform_interface: ^2.1.2 diff --git a/packages/local_auth/local_auth/test/local_auth_test.dart b/packages/local_auth/local_auth/test/local_auth_test.dart index b92297d90231..d3f92dfe95db 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -4,13 +4,14 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:local_auth/src/local_auth.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_ios/local_auth_ios.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:mockito/mockito.dart'; -import 'package:platform/platform.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; void main() { @@ -24,24 +25,6 @@ void main() { LocalAuthPlatform.instance = mockLocalAuthPlatform; }); - test('authenticateWithBiometrics calls platform implementation', () { - when(mockLocalAuthPlatform.authenticate( - localizedReason: anyNamed('localizedReason'), - authMessages: anyNamed('authMessages'), - options: anyNamed('options'), - )).thenAnswer((_) async => true); - localAuthentication.authenticateWithBiometrics( - localizedReason: 'Test Reason'); - verify(mockLocalAuthPlatform.authenticate( - localizedReason: 'Test Reason', - authMessages: [ - const IOSAuthMessages(), - const AndroidAuthMessages(), - ], - options: const AuthenticationOptions(biometricOnly: true), - )).called(1); - }); - test('authenticate calls platform implementation', () { when(mockLocalAuthPlatform.authenticate( localizedReason: anyNamed('localizedReason'), @@ -73,20 +56,13 @@ void main() { verify(mockLocalAuthPlatform.getEnrolledBiometrics()).called(1); }); - test('stopAuthentication calls platform implementation on Android', () { + test('stopAuthentication calls platform implementation', () { when(mockLocalAuthPlatform.stopAuthentication()) .thenAnswer((_) async => true); - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); localAuthentication.stopAuthentication(); verify(mockLocalAuthPlatform.stopAuthentication()).called(1); }); - test('stopAuthentication does not call platform implementation on iOS', () { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - localAuthentication.stopAuthentication(); - verifyNever(mockLocalAuthPlatform.stopAuthentication()); - }); - test('canCheckBiometrics returns correct result', () async { when(mockLocalAuthPlatform.deviceSupportsBiometrics()) .thenAnswer((_) async => false); @@ -110,11 +86,8 @@ class MockLocalAuthPlatform extends Mock @override Future authenticate({ - String? localizedReason, - Iterable? authMessages = const [ - IOSAuthMessages(), - AndroidAuthMessages() - ], + required String? localizedReason, + required Iterable? authMessages, AuthenticationOptions? options = const AuthenticationOptions(), }) => super.noSuchMethod(