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

Commit 2efdee1

Browse files
authored
[local_auth] Add platform interface to prepare for migration to federated architecture (#4697)
1 parent 07578ae commit 2efdee1

11 files changed

Lines changed: 599 additions & 0 deletions
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Below is a list of people and organizations that have contributed
2+
# to the Flutter project. Names should be added to the list like so:
3+
#
4+
# Name/Organization <email address>
5+
6+
Google Inc.
7+
The Chromium Authors
8+
German Saprykin <[email protected]>
9+
Benjamin Sauer <[email protected]>
10+
11+
Ali Bitek <[email protected]>
12+
Pol Batlló <[email protected]>
13+
Anatoly Pulyaevskiy
14+
Hayden Flinner <[email protected]>
15+
Stefano Rodriguez <[email protected]>
16+
Salvatore Giordano <[email protected]>
17+
Brian Armstrong <[email protected]>
18+
Paul DeMarco <[email protected]>
19+
Fabricio Nogueira <[email protected]>
20+
Simon Lightfoot <[email protected]>
21+
Ashton Thomas <[email protected]>
22+
Thomas Danner <[email protected]>
23+
Diego Velásquez <[email protected]>
24+
Hajime Nakamura <[email protected]>
25+
Tuyển Vũ Xuân <[email protected]>
26+
Miguel Ruivo <[email protected]>
27+
Sarthak Verma <[email protected]>
28+
Mike Diarmid <[email protected]>
29+
Invertase <[email protected]>
30+
Elliot Hesp <[email protected]>
31+
Vince Varga <[email protected]>
32+
Aawaz Gyawali <[email protected]>
33+
EUI Limited <[email protected]>
34+
Katarina Sheremet <[email protected]>
35+
Thomas Stockx <[email protected]>
36+
Sarbagya Dhaubanjar <[email protected]>
37+
Ozkan Eksi <[email protected]>
38+
Rishab Nayak <[email protected]>
39+
40+
Jonathan Younger <[email protected]>
41+
Jose Sanchez <[email protected]>
42+
Debkanchan Samadder <[email protected]>
43+
Audrius Karosevicius <[email protected]>
44+
Lukasz Piliszczuk <[email protected]>
45+
SoundReply Solutions GmbH <[email protected]>
46+
Rafal Wachol <[email protected]>
47+
Pau Picas <[email protected]>
48+
Christian Weder <[email protected]>
49+
Alexandru Tuca <[email protected]>
50+
Christian Weder <[email protected]>
51+
Rhodes Davis Jr. <[email protected]>
52+
Luigi Agosti <[email protected]>
53+
Quentin Le Guennec <[email protected]>
54+
Koushik Ravikumar <[email protected]>
55+
Nissim Dsilva <[email protected]>
56+
Giancarlo Rocha <[email protected]>
57+
Ryo Miyake <[email protected]>
58+
Théo Champion <[email protected]>
59+
Kazuki Yamaguchi <[email protected]>
60+
Eitan Schwartz <[email protected]>
61+
Chris Rutkowski <[email protected]>
62+
Juan Alvarez <[email protected]>
63+
Aleksandr Yurkovskiy <[email protected]>
64+
Anton Borries <[email protected]>
65+
66+
Rahul Raj <[email protected]>
67+
Bodhi Mulders <[email protected]>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 1.0.0
2+
3+
* Initial release.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright 2013 The Flutter Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above
9+
copyright notice, this list of conditions and the following
10+
disclaimer in the documentation and/or other materials provided
11+
with the distribution.
12+
* Neither the name of Google Inc. nor the names of its
13+
contributors may be used to endorse or promote products derived
14+
from this software without specific prior written permission.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# local_auth_platform_interface
2+
3+
A common platform interface for the [`local_auth`][1] plugin.
4+
5+
This interface allows platform-specific implementations of the `local_auth`
6+
plugin, as well as the plugin itself, to ensure they are supporting the
7+
same interface.
8+
9+
# Usage
10+
11+
To implement a new platform-specific implementation of `local_auth`, extend
12+
[`LocalAuthPlatform`][2] with an implementation that performs the
13+
platform-specific behavior, and when you register your plugin, set the default
14+
`LocalAuthPlatform` by calling
15+
`LocalAuthPlatform.instance = MyLocalAuthPlatform()`.
16+
17+
# Note on breaking changes
18+
19+
Strongly prefer non-breaking changes (such as adding a method to the interface)
20+
over breaking changes for this package.
21+
22+
See https://flutter.dev/go/platform-interface-breaking-changes for a discussion
23+
on why a less-clean interface is preferable to a breaking change.
24+
25+
[1]: ../local_auth
26+
[2]: lib/local_auth_platform_interface.dart
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2013 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 'package:flutter/services.dart';
6+
import 'package:local_auth_platform_interface/local_auth_platform_interface.dart';
7+
import 'package:local_auth_platform_interface/types/auth_messages.dart';
8+
import 'package:local_auth_platform_interface/types/auth_options.dart';
9+
import 'package:local_auth_platform_interface/types/biometric_type.dart';
10+
11+
const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth');
12+
13+
/// The default interface implementation acting as a placeholder for
14+
/// the native implementation to be set.
15+
///
16+
/// This implementation is not used by any of the implementations in this
17+
/// repository, and exists only for backward compatibility with any
18+
/// clients that were relying on internal details of the method channel
19+
/// in the pre-federated plugin.
20+
class DefaultLocalAuthPlatform extends LocalAuthPlatform {
21+
@override
22+
Future<bool> authenticate({
23+
required String localizedReason,
24+
required Iterable<AuthMessages> authMessages,
25+
AuthenticationOptions options = const AuthenticationOptions(),
26+
}) async {
27+
assert(localizedReason.isNotEmpty);
28+
final Map<String, Object> args = <String, Object>{
29+
'localizedReason': localizedReason,
30+
'useErrorDialogs': options.useErrorDialogs,
31+
'stickyAuth': options.stickyAuth,
32+
'sensitiveTransaction': options.sensitiveTransaction,
33+
'biometricOnly': options.biometricOnly,
34+
};
35+
for (final AuthMessages messages in authMessages) {
36+
args.addAll(messages.args);
37+
}
38+
return (await _channel.invokeMethod<bool>('authenticate', args)) ?? false;
39+
}
40+
41+
@override
42+
Future<List<BiometricType>> getEnrolledBiometrics() async {
43+
final List<String> result = (await _channel.invokeListMethod<String>(
44+
'getAvailableBiometrics',
45+
)) ??
46+
<String>[];
47+
final List<BiometricType> biometrics = <BiometricType>[];
48+
for (final String value in result) {
49+
switch (value) {
50+
case 'face':
51+
biometrics.add(BiometricType.face);
52+
break;
53+
case 'fingerprint':
54+
biometrics.add(BiometricType.fingerprint);
55+
break;
56+
case 'iris':
57+
biometrics.add(BiometricType.iris);
58+
break;
59+
case 'undefined':
60+
break;
61+
}
62+
}
63+
return biometrics;
64+
}
65+
66+
@override
67+
Future<bool> deviceSupportsBiometrics() async {
68+
return (await getEnrolledBiometrics()).isNotEmpty;
69+
}
70+
71+
@override
72+
Future<bool> isDeviceSupported() async =>
73+
(await _channel.invokeMethod<bool>('isDeviceSupported')) ?? false;
74+
75+
@override
76+
Future<bool> stopAuthentication() async =>
77+
await _channel.invokeMethod<bool>('stopAuthentication') ?? false;
78+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2013 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 'package:local_auth_platform_interface/default_method_channel_platform.dart';
6+
import 'package:local_auth_platform_interface/types/auth_messages.dart';
7+
import 'package:local_auth_platform_interface/types/auth_options.dart';
8+
import 'package:local_auth_platform_interface/types/biometric_type.dart';
9+
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
10+
11+
/// The interface that implementations of local_auth must implement.
12+
///
13+
/// Platform implementations should extend this class rather than implement it as `local_auth`
14+
/// does not consider newly added methods to be breaking changes. Extending this class
15+
/// (using `extends`) ensures that the subclass will get the default implementation, while
16+
/// platform implementations that `implements` this interface will be broken by newly added
17+
/// [LocalAuthPlatform] methods.
18+
abstract class LocalAuthPlatform extends PlatformInterface {
19+
/// Constructs a LocalAuthPlatform.
20+
LocalAuthPlatform() : super(token: _token);
21+
22+
static final Object _token = Object();
23+
24+
static LocalAuthPlatform _instance = DefaultLocalAuthPlatform();
25+
26+
/// The default instance of [LocalAuthPlatform] to use.
27+
///
28+
/// Defaults to [DefaultLocalAuthPlatform].
29+
static LocalAuthPlatform get instance => _instance;
30+
31+
/// Platform-specific implementations should set this with their own
32+
/// platform-specific class that extends [LocalAuthPlatform] when they
33+
/// register themselves.
34+
static set instance(LocalAuthPlatform instance) {
35+
PlatformInterface.verifyToken(instance, _token);
36+
_instance = instance;
37+
}
38+
39+
/// Authenticates the user with biometrics available on the device while also
40+
/// allowing the user to use device authentication - pin, pattern, passcode.
41+
///
42+
/// Returns true if the user successfully authenticated, false otherwise.
43+
///
44+
/// [localizedReason] is the message to show to user while prompting them
45+
/// for authentication. This is typically along the lines of: 'Please scan
46+
/// your finger to access MyApp.'. This must not be empty.
47+
///
48+
/// Provide [authMessages] if you want to
49+
/// customize messages in the dialogs.
50+
///
51+
/// Provide [options] for configuring further authentication related options.
52+
///
53+
/// Throws a [PlatformException] if there were technical problems with local
54+
/// authentication (e.g. lack of relevant hardware). This might throw
55+
/// [PlatformException] with error code [otherOperatingSystem] on the iOS
56+
/// simulator.
57+
Future<bool> authenticate({
58+
required String localizedReason,
59+
required Iterable<AuthMessages> authMessages,
60+
AuthenticationOptions options = const AuthenticationOptions(),
61+
}) async {
62+
throw UnimplementedError('authenticate() has not been implemented.');
63+
}
64+
65+
/// Returns true if the device is capable of checking biometrics.
66+
///
67+
/// This will return true even if there are no biometrics currently enrolled.
68+
Future<bool> deviceSupportsBiometrics() async {
69+
throw UnimplementedError('canCheckBiometrics() has not been implemented.');
70+
}
71+
72+
/// Returns a list of enrolled biometrics.
73+
///
74+
/// Possible values include:
75+
/// - BiometricType.face
76+
/// - BiometricType.fingerprint
77+
/// - BiometricType.iris (not yet implemented)
78+
/// - BiometricType.strong
79+
/// - BiometricType.weak
80+
Future<List<BiometricType>> getEnrolledBiometrics() async {
81+
throw UnimplementedError(
82+
'getAvailableBiometrics() has not been implemented.');
83+
}
84+
85+
/// Returns true if device is capable of checking biometrics or is able to
86+
/// fail over to device credentials.
87+
Future<bool> isDeviceSupported() async {
88+
throw UnimplementedError('isDeviceSupported() has not been implemented.');
89+
}
90+
91+
/// Cancels any authentication currently in progress.
92+
///
93+
/// Returns true if auth was cancelled successfully.
94+
/// Returns false if there was no authentication in progress,
95+
/// or an error occurred.
96+
Future<bool> stopAuthentication() async {
97+
throw UnimplementedError('stopAuthentication() has not been implemented.');
98+
}
99+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2013 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+
/// Abstract class for storing platform specific strings.
6+
abstract class AuthMessages {
7+
/// Constructs an instance of [AuthMessages].
8+
const AuthMessages();
9+
10+
/// Returns all platform-specific messages as a map.
11+
Map<String, String> get args;
12+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2013 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 'package:flutter/foundation.dart';
6+
7+
/// Options wrapper for [LocalAuthPlatform.authenticate] parameters.
8+
@immutable
9+
class AuthenticationOptions {
10+
/// Constructs a new instance.
11+
const AuthenticationOptions({
12+
this.useErrorDialogs = true,
13+
this.stickyAuth = false,
14+
this.sensitiveTransaction = true,
15+
this.biometricOnly = false,
16+
});
17+
18+
/// Whether the system will attempt to handle user-fixable issues encountered
19+
/// while authenticating. For instance, if a fingerprint reader exists on the
20+
/// device but there's no fingerprint registered, the plugin might attempt to
21+
/// take the user to settings to add one. Anything that is not user fixable,
22+
/// such as no biometric sensor on device, will still result in
23+
/// a [PlatformException].
24+
final bool useErrorDialogs;
25+
26+
/// Used when the application goes into background for any reason while the
27+
/// authentication is in progress. Due to security reasons, the
28+
/// authentication has to be stopped at that time. If stickyAuth is set to
29+
/// true, authentication resumes when the app is resumed. If it is set to
30+
/// false (default), then as soon as app is paused a failure message is sent
31+
/// back to Dart and it is up to the client app to restart authentication or
32+
/// do something else.
33+
final bool stickyAuth;
34+
35+
/// Whether platform specific precautions are enabled. For instance, on face
36+
/// unlock, Android opens a confirmation dialog after the face is recognized
37+
/// to make sure the user meant to unlock their device.
38+
final bool sensitiveTransaction;
39+
40+
/// Prevent authentications from using non-biometric local authentication
41+
/// such as pin, passcode, or pattern.
42+
final bool biometricOnly;
43+
44+
@override
45+
bool operator ==(Object other) =>
46+
identical(this, other) ||
47+
other is AuthenticationOptions &&
48+
runtimeType == other.runtimeType &&
49+
useErrorDialogs == other.useErrorDialogs &&
50+
stickyAuth == other.stickyAuth &&
51+
sensitiveTransaction == other.sensitiveTransaction &&
52+
biometricOnly == other.biometricOnly;
53+
54+
@override
55+
int get hashCode =>
56+
useErrorDialogs.hashCode ^
57+
stickyAuth.hashCode ^
58+
sensitiveTransaction.hashCode ^
59+
biometricOnly.hashCode;
60+
}

0 commit comments

Comments
 (0)