expo-local-authentication 13.0.2 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/android/build.gradle +2 -2
- package/build/ExpoLocalAuthentication.d.ts +1 -1
- package/build/ExpoLocalAuthentication.d.ts.map +1 -1
- package/build/ExpoLocalAuthentication.js +2 -2
- package/build/ExpoLocalAuthentication.js.map +1 -1
- package/build/LocalAuthentication.types.d.ts +2 -2
- package/build/LocalAuthentication.types.d.ts.map +1 -1
- package/expo-module.config.json +7 -0
- package/ios/{EXLocalAuthentication.podspec → ExpoLocalAuthentication.podspec} +10 -3
- package/ios/LocalAuthenticationModule.swift +169 -0
- package/ios/LocalAuthenticationOptions.swift +8 -0
- package/package.json +2 -2
- package/src/ExpoLocalAuthentication.ts +2 -2
- package/ios/EXLocalAuthentication/EXLocalAuthentication.h +0 -15
- package/ios/EXLocalAuthentication/EXLocalAuthentication.m +0 -206
- package/unimodule.json +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 13.1.0 — 2022-12-30
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Native module on iOS is now written in Swift using the Sweet API. ([#19980](https://github.com/expo/expo/pull/19980) by [@fobos531](https://github.com/fobos531))
|
|
18
|
+
|
|
13
19
|
## 13.0.2 — 2022-11-02
|
|
14
20
|
|
|
15
21
|
_This version does not introduce any user-facing changes._
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Provides an API for FaceID and TouchID (iOS) or the Fingerprint API (Android) to
|
|
|
4
4
|
|
|
5
5
|
# API documentation
|
|
6
6
|
|
|
7
|
-
- [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/local-authentication.
|
|
7
|
+
- [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/local-authentication.mdx)
|
|
8
8
|
- [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/local-authentication/)
|
|
9
9
|
|
|
10
10
|
# Installation in managed Expo projects
|
package/android/build.gradle
CHANGED
|
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
|
|
|
3
3
|
apply plugin: 'maven-publish'
|
|
4
4
|
|
|
5
5
|
group = 'host.exp.exponent'
|
|
6
|
-
version = '13.0
|
|
6
|
+
version = '13.1.0'
|
|
7
7
|
|
|
8
8
|
buildscript {
|
|
9
9
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
@@ -74,7 +74,7 @@ android {
|
|
|
74
74
|
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
75
75
|
targetSdkVersion safeExtGet("targetSdkVersion", 31)
|
|
76
76
|
versionCode 30
|
|
77
|
-
versionName "13.0
|
|
77
|
+
versionName "13.1.0"
|
|
78
78
|
}
|
|
79
79
|
lintOptions {
|
|
80
80
|
abortOnError false
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoLocalAuthentication.d.ts","sourceRoot":"","sources":["../src/ExpoLocalAuthentication.ts"],"names":[],"mappings":";AAEA,
|
|
1
|
+
{"version":3,"file":"ExpoLocalAuthentication.d.ts","sourceRoot":"","sources":["../src/ExpoLocalAuthentication.ts"],"names":[],"mappings":";AAEA,wBAA8D"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
export default requireNativeModule('ExpoLocalAuthentication');
|
|
3
3
|
//# sourceMappingURL=ExpoLocalAuthentication.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoLocalAuthentication.js","sourceRoot":"","sources":["../src/ExpoLocalAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ExpoLocalAuthentication.js","sourceRoot":"","sources":["../src/ExpoLocalAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAe,mBAAmB,CAAC,yBAAyB,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\n\nexport default requireNativeModule('ExpoLocalAuthentication');\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type LocalAuthenticationResult = {
|
|
2
2
|
success: true;
|
|
3
3
|
} | {
|
|
4
4
|
success: false;
|
|
@@ -34,7 +34,7 @@ export declare enum SecurityLevel {
|
|
|
34
34
|
*/
|
|
35
35
|
BIOMETRIC = 2
|
|
36
36
|
}
|
|
37
|
-
export
|
|
37
|
+
export type LocalAuthenticationOptions = {
|
|
38
38
|
/**
|
|
39
39
|
* A message that is shown alongside the TouchID or FaceID prompt.
|
|
40
40
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalAuthentication.types.d.ts","sourceRoot":"","sources":["../src/LocalAuthentication.types.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"LocalAuthentication.types.d.ts","sourceRoot":"","sources":["../src/LocalAuthentication.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,yBAAyB,GACjC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAGxD,oBAAY,kBAAkB;IAC5B;;OAEG;IACH,WAAW,IAAI;IACf;;OAEG;IACH,kBAAkB,IAAI;IACtB;;;OAGG;IACH,IAAI,IAAI;CACT;AAGD,oBAAY,aAAa;IACvB;;OAEG;IACH,IAAI,IAAI;IACR;;OAEG;IACH,MAAM,IAAI;IACV;;OAEG;IACH,SAAS,IAAI;CACd;AAGD,MAAM,MAAM,0BAA0B,GAAG;IACvC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC"}
|
|
@@ -3,7 +3,7 @@ require 'json'
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
4
|
|
|
5
5
|
Pod::Spec.new do |s|
|
|
6
|
-
s.name = '
|
|
6
|
+
s.name = 'ExpoLocalAuthentication'
|
|
7
7
|
s.version = package['version']
|
|
8
8
|
s.summary = package['description']
|
|
9
9
|
s.description = package['description']
|
|
@@ -11,15 +11,22 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.author = package['author']
|
|
12
12
|
s.homepage = package['homepage']
|
|
13
13
|
s.platform = :ios, '13.0'
|
|
14
|
+
s.swift_version = '5.4'
|
|
14
15
|
s.source = { git: 'https://github.com/expo/expo.git' }
|
|
15
16
|
s.static_framework = true
|
|
16
17
|
|
|
17
18
|
s.dependency 'ExpoModulesCore'
|
|
18
19
|
|
|
20
|
+
# Swift/Objective-C compatibility
|
|
21
|
+
s.pod_target_xcconfig = {
|
|
22
|
+
'DEFINES_MODULE' => 'YES',
|
|
23
|
+
'SWIFT_COMPILATION_MODE' => 'wholemodule'
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
|
|
20
|
-
s.source_files = "
|
|
27
|
+
s.source_files = "**/*.h"
|
|
21
28
|
s.vendored_frameworks = "#{s.name}.xcframework"
|
|
22
29
|
else
|
|
23
|
-
s.source_files = "
|
|
30
|
+
s.source_files = "**/*.{h,m,swift}"
|
|
24
31
|
end
|
|
25
32
|
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import LocalAuthentication
|
|
3
|
+
|
|
4
|
+
public class LocalAuthenticationModule: Module {
|
|
5
|
+
public func definition() -> ModuleDefinition {
|
|
6
|
+
Name("ExpoLocalAuthentication")
|
|
7
|
+
|
|
8
|
+
AsyncFunction("hasHardwareAsync") { () -> Bool in
|
|
9
|
+
let context = LAContext()
|
|
10
|
+
var error: NSError?
|
|
11
|
+
let isSupported: Bool = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)
|
|
12
|
+
let isAvailable: Bool = isSupported || error?.code != LAError.biometryNotAvailable.rawValue
|
|
13
|
+
|
|
14
|
+
return isAvailable
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
AsyncFunction("isEnrolledAsync") { () -> Bool in
|
|
18
|
+
let context = LAContext()
|
|
19
|
+
var error: NSError?
|
|
20
|
+
let isSupported: Bool = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)
|
|
21
|
+
let isEnrolled: Bool = isSupported && error == nil
|
|
22
|
+
|
|
23
|
+
return isEnrolled
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
AsyncFunction("supportedAuthenticationTypesAsync") { () -> [Int] in
|
|
27
|
+
var supportedAuthenticationTypes: [Int] = []
|
|
28
|
+
|
|
29
|
+
if isTouchIdDevice() {
|
|
30
|
+
supportedAuthenticationTypes.append(AuthenticationType.fingerprint.rawValue)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if isFaceIdDevice() {
|
|
34
|
+
supportedAuthenticationTypes.append(AuthenticationType.facialRecognition.rawValue)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return supportedAuthenticationTypes
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
AsyncFunction("getEnrolledLevelAsync") { () -> Int in
|
|
41
|
+
let context = LAContext()
|
|
42
|
+
var error: NSError?
|
|
43
|
+
|
|
44
|
+
var level: Int = SecurityLevel.none.rawValue
|
|
45
|
+
|
|
46
|
+
let isAuthenticationSupported: Bool = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: &error)
|
|
47
|
+
if isAuthenticationSupported && error == nil {
|
|
48
|
+
level = SecurityLevel.secret.rawValue
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let isBiometricsSupported: Bool = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)
|
|
52
|
+
|
|
53
|
+
if isBiometricsSupported && error == nil {
|
|
54
|
+
level = SecurityLevel.biometric.rawValue
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return level
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
AsyncFunction("authenticateAsync") { (options: LocalAuthenticationOptions, promise: Promise) -> Void in
|
|
61
|
+
var warningMessage: NSString?
|
|
62
|
+
var reason = options.promptMessage
|
|
63
|
+
var cancelLabel = options.cancelLabel
|
|
64
|
+
var fallbackLabel = options.fallbackLabel
|
|
65
|
+
var disableDeviceFallback = options.disableDeviceFallback
|
|
66
|
+
|
|
67
|
+
if isFaceIdDevice() {
|
|
68
|
+
let usageDescription = Bundle.main.object(forInfoDictionaryKey: "NSFaceIDUsageDescription")
|
|
69
|
+
|
|
70
|
+
if usageDescription != nil {
|
|
71
|
+
warningMessage = "FaceID is available but has not been configured. To enable FaceID, provide `NSFaceIDUsageDescription`."
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let context = LAContext()
|
|
76
|
+
|
|
77
|
+
if fallbackLabel != nil {
|
|
78
|
+
context.localizedFallbackTitle = fallbackLabel
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if cancelLabel != nil {
|
|
82
|
+
context.localizedCancelTitle = cancelLabel
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
context.interactionNotAllowed = false
|
|
86
|
+
|
|
87
|
+
let policyForAuth: LAPolicy = disableDeviceFallback ? LAPolicy.deviceOwnerAuthenticationWithBiometrics : LAPolicy.deviceOwnerAuthentication
|
|
88
|
+
|
|
89
|
+
if disableDeviceFallback {
|
|
90
|
+
if warningMessage != nil {
|
|
91
|
+
// If the warning message is set (NSFaceIDUsageDescription is not configured) then we can't use
|
|
92
|
+
// authentication with biometrics — it would crash, so let's just resolve with no success.
|
|
93
|
+
// We could reject, but we already resolve even if there are any errors, so sadly we would need to introduce a breaking change.
|
|
94
|
+
return promise.resolve([
|
|
95
|
+
"success": false,
|
|
96
|
+
"error": "missing_usage_description",
|
|
97
|
+
"warning": warningMessage
|
|
98
|
+
])
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
context.evaluatePolicy(policyForAuth, localizedReason: reason ?? "") { success, error in
|
|
103
|
+
var err: String?
|
|
104
|
+
|
|
105
|
+
if let error = error as? NSError {
|
|
106
|
+
err = convertErrorCode(error: error)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return promise.resolve([
|
|
110
|
+
"success": success,
|
|
111
|
+
"error": err,
|
|
112
|
+
"warning": warningMessage
|
|
113
|
+
])
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
func isFaceIdDevice() -> Bool {
|
|
120
|
+
let context = LAContext()
|
|
121
|
+
context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: nil)
|
|
122
|
+
|
|
123
|
+
return context.biometryType == LABiometryType.faceID
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
func isTouchIdDevice() -> Bool {
|
|
127
|
+
let context = LAContext()
|
|
128
|
+
context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: nil)
|
|
129
|
+
|
|
130
|
+
return context.biometryType == LABiometryType.touchID
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
func convertErrorCode(error: NSError) -> String {
|
|
134
|
+
switch error.code {
|
|
135
|
+
case LAError.systemCancel.rawValue:
|
|
136
|
+
return "system_cancel"
|
|
137
|
+
case LAError.appCancel.rawValue:
|
|
138
|
+
return "app_cancel"
|
|
139
|
+
case LAError.biometryLockout.rawValue:
|
|
140
|
+
return "lockout"
|
|
141
|
+
case LAError.userFallback.rawValue:
|
|
142
|
+
return "user_fallback"
|
|
143
|
+
case LAError.userCancel.rawValue:
|
|
144
|
+
return "user_cancel"
|
|
145
|
+
case LAError.biometryNotAvailable.rawValue:
|
|
146
|
+
return "not_available"
|
|
147
|
+
case LAError.invalidContext.rawValue:
|
|
148
|
+
return "invalid_context"
|
|
149
|
+
case LAError.biometryNotEnrolled.rawValue:
|
|
150
|
+
return "not_enrolled"
|
|
151
|
+
case LAError.passcodeNotSet.rawValue:
|
|
152
|
+
return "passcode_not_set"
|
|
153
|
+
case LAError.authenticationFailed.rawValue:
|
|
154
|
+
return "authentication_failed"
|
|
155
|
+
default:
|
|
156
|
+
return "unknown: \(error.code), \(error.localizedDescription)"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
enum AuthenticationType: Int {
|
|
161
|
+
case fingerprint = 1
|
|
162
|
+
case facialRecognition = 2
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
enum SecurityLevel: Int {
|
|
166
|
+
case none = 0
|
|
167
|
+
case secret = 1
|
|
168
|
+
case biometric = 2
|
|
169
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-local-authentication",
|
|
3
|
-
"version": "13.0
|
|
3
|
+
"version": "13.1.0",
|
|
4
4
|
"description": "Provides an API for FaceID and TouchID (iOS) or the Fingerprint API (Android) to authenticate the user with a face or fingerprint scan.",
|
|
5
5
|
"main": "build/LocalAuthentication.js",
|
|
6
6
|
"types": "build/LocalAuthentication.d.ts",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"expo": "*"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "ba80e8181b79d06e00a245653727f4eaeb80420e"
|
|
50
50
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
2
|
|
|
3
|
-
export default
|
|
3
|
+
export default requireNativeModule('ExpoLocalAuthentication');
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
2
|
-
|
|
3
|
-
#import <ExpoModulesCore/EXExportedModule.h>
|
|
4
|
-
#import <ExpoModulesCore/EXModuleRegistryConsumer.h>
|
|
5
|
-
|
|
6
|
-
@interface EXLocalAuthentication : EXExportedModule
|
|
7
|
-
|
|
8
|
-
- (void)authenticateWithOptions:(NSDictionary *)options
|
|
9
|
-
resolve:(EXPromiseResolveBlock)resolve
|
|
10
|
-
reject:(EXPromiseRejectBlock)reject;
|
|
11
|
-
- (NSString *)convertErrorCode:(NSError *)error;
|
|
12
|
-
+ (BOOL)isTouchIdDevice;
|
|
13
|
-
+ (BOOL)isFaceIdDevice;
|
|
14
|
-
|
|
15
|
-
@end
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
2
|
-
|
|
3
|
-
#import <LocalAuthentication/LocalAuthentication.h>
|
|
4
|
-
|
|
5
|
-
#import <ExpoModulesCore/EXUtilities.h>
|
|
6
|
-
#import <EXLocalAuthentication/EXLocalAuthentication.h>
|
|
7
|
-
|
|
8
|
-
typedef NS_ENUM(NSInteger, EXAuthenticationType) {
|
|
9
|
-
EXAuthenticationTypeFingerprint = 1,
|
|
10
|
-
EXAuthenticationTypeFacialRecognition = 2,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
typedef NS_ENUM(NSInteger, EXSecurityLevel) {
|
|
14
|
-
EXSecurityLevelNone = 0,
|
|
15
|
-
EXSecurityLevelSecret = 1,
|
|
16
|
-
EXSecurityLevelBiometric = 2,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
@implementation EXLocalAuthentication
|
|
20
|
-
|
|
21
|
-
EX_EXPORT_MODULE(ExpoLocalAuthentication)
|
|
22
|
-
|
|
23
|
-
EX_EXPORT_METHOD_AS(supportedAuthenticationTypesAsync,
|
|
24
|
-
supportedAuthenticationTypesAsync:(EXPromiseResolveBlock)resolve
|
|
25
|
-
reject:(EXPromiseRejectBlock)reject)
|
|
26
|
-
{
|
|
27
|
-
NSMutableArray *results = [NSMutableArray array];
|
|
28
|
-
if ([[self class] isTouchIdDevice]) {
|
|
29
|
-
[results addObject:@(EXAuthenticationTypeFingerprint)];
|
|
30
|
-
}
|
|
31
|
-
if ([[self class] isFaceIdDevice]) {
|
|
32
|
-
[results addObject:@(EXAuthenticationTypeFacialRecognition)];
|
|
33
|
-
}
|
|
34
|
-
resolve(results);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
EX_EXPORT_METHOD_AS(hasHardwareAsync,
|
|
38
|
-
hasHardwareAsync:(EXPromiseResolveBlock)resolve
|
|
39
|
-
reject:(EXPromiseRejectBlock)reject)
|
|
40
|
-
{
|
|
41
|
-
LAContext *context = [LAContext new];
|
|
42
|
-
NSError *error = nil;
|
|
43
|
-
|
|
44
|
-
BOOL isSupported = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
|
|
45
|
-
BOOL isAvailable = isSupported || error.code != LAErrorBiometryNotAvailable;
|
|
46
|
-
|
|
47
|
-
resolve(@(isAvailable));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
EX_EXPORT_METHOD_AS(isEnrolledAsync,
|
|
51
|
-
isEnrolledAsync:(EXPromiseResolveBlock)resolve
|
|
52
|
-
reject:(EXPromiseRejectBlock)reject)
|
|
53
|
-
{
|
|
54
|
-
LAContext *context = [LAContext new];
|
|
55
|
-
NSError *error = nil;
|
|
56
|
-
|
|
57
|
-
BOOL isSupported = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
|
|
58
|
-
BOOL isEnrolled = isSupported && error == nil;
|
|
59
|
-
|
|
60
|
-
resolve(@(isEnrolled));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
EX_EXPORT_METHOD_AS(getEnrolledLevelAsync,
|
|
64
|
-
getEnrolledLevelAsync:(EXPromiseResolveBlock)resolve
|
|
65
|
-
reject:(EXPromiseRejectBlock)reject)
|
|
66
|
-
{
|
|
67
|
-
LAContext *context = [LAContext new];
|
|
68
|
-
NSError *error = nil;
|
|
69
|
-
|
|
70
|
-
int level = EXSecurityLevelNone;
|
|
71
|
-
|
|
72
|
-
BOOL isAuthenticationSupported = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error];
|
|
73
|
-
if (isAuthenticationSupported && error == nil) {
|
|
74
|
-
level = EXSecurityLevelSecret;
|
|
75
|
-
}
|
|
76
|
-
BOOL isBiometricsSupported = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
|
|
77
|
-
if (isBiometricsSupported && error == nil) {
|
|
78
|
-
level = EXSecurityLevelBiometric;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
resolve(@(level));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
EX_EXPORT_METHOD_AS(authenticateAsync,
|
|
85
|
-
authenticateWithOptions:(NSDictionary *)options
|
|
86
|
-
resolve:(EXPromiseResolveBlock)resolve
|
|
87
|
-
reject:(EXPromiseRejectBlock)reject)
|
|
88
|
-
{
|
|
89
|
-
NSString *warningMessage;
|
|
90
|
-
NSString *reason = options[@"promptMessage"];
|
|
91
|
-
NSString *cancelLabel = options[@"cancelLabel"];
|
|
92
|
-
NSString *fallbackLabel = options[@"fallbackLabel"];
|
|
93
|
-
NSString *disableDeviceFallback = options[@"disableDeviceFallback"];
|
|
94
|
-
|
|
95
|
-
if ([[self class] isFaceIdDevice]) {
|
|
96
|
-
NSString *usageDescription = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"NSFaceIDUsageDescription"];
|
|
97
|
-
|
|
98
|
-
if (!usageDescription) {
|
|
99
|
-
warningMessage = @"FaceID is available but has not been configured. To enable FaceID, provide `NSFaceIDUsageDescription`.";
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
LAContext *context = [LAContext new];
|
|
104
|
-
|
|
105
|
-
if (fallbackLabel != nil) {
|
|
106
|
-
context.localizedFallbackTitle = fallbackLabel;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (cancelLabel != nil) {
|
|
110
|
-
context.localizedCancelTitle = cancelLabel;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
context.interactionNotAllowed = false;
|
|
114
|
-
|
|
115
|
-
if ([disableDeviceFallback boolValue]) {
|
|
116
|
-
if (warningMessage) {
|
|
117
|
-
// If the warning message is set (NSFaceIDUsageDescription is not configured) then we can't use
|
|
118
|
-
// authentication with biometrics — it would crash, so let's just resolve with no success.
|
|
119
|
-
// We could reject, but we already resolve even if there are any errors, so sadly we would need to introduce a breaking change.
|
|
120
|
-
return resolve(@{
|
|
121
|
-
@"success": @NO,
|
|
122
|
-
@"error": @"missing_usage_description",
|
|
123
|
-
@"warning": warningMessage
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
|
|
127
|
-
localizedReason:reason
|
|
128
|
-
reply:^(BOOL success, NSError *error) {
|
|
129
|
-
resolve(@{
|
|
130
|
-
@"success": @(success),
|
|
131
|
-
@"error": error == nil ? [NSNull null] : [self convertErrorCode:error],
|
|
132
|
-
@"warning": EXNullIfNil(warningMessage),
|
|
133
|
-
});
|
|
134
|
-
}];
|
|
135
|
-
} else {
|
|
136
|
-
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication
|
|
137
|
-
localizedReason:reason
|
|
138
|
-
reply:^(BOOL success, NSError *error) {
|
|
139
|
-
resolve(@{
|
|
140
|
-
@"success": @(success),
|
|
141
|
-
@"error": error == nil ? [NSNull null] : [self convertErrorCode:error],
|
|
142
|
-
@"warning": EXNullIfNil(warningMessage),
|
|
143
|
-
});
|
|
144
|
-
}];
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
- (NSString *)convertErrorCode:(NSError *)error
|
|
150
|
-
{
|
|
151
|
-
switch (error.code) {
|
|
152
|
-
case LAErrorSystemCancel:
|
|
153
|
-
return @"system_cancel";
|
|
154
|
-
case LAErrorAppCancel:
|
|
155
|
-
return @"app_cancel";
|
|
156
|
-
case LAErrorTouchIDLockout:
|
|
157
|
-
return @"lockout";
|
|
158
|
-
case LAErrorUserFallback:
|
|
159
|
-
return @"user_fallback";
|
|
160
|
-
case LAErrorUserCancel:
|
|
161
|
-
return @"user_cancel";
|
|
162
|
-
case LAErrorTouchIDNotAvailable:
|
|
163
|
-
return @"not_available";
|
|
164
|
-
case LAErrorInvalidContext:
|
|
165
|
-
return @"invalid_context";
|
|
166
|
-
case LAErrorTouchIDNotEnrolled:
|
|
167
|
-
return @"not_enrolled";
|
|
168
|
-
case LAErrorPasscodeNotSet:
|
|
169
|
-
return @"passcode_not_set";
|
|
170
|
-
case LAErrorAuthenticationFailed:
|
|
171
|
-
return @"authentication_failed";
|
|
172
|
-
default:
|
|
173
|
-
return [@"unknown: " stringByAppendingFormat:@"%ld, %@", (long) error.code, error.localizedDescription];
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
+ (BOOL)isFaceIdDevice
|
|
178
|
-
{
|
|
179
|
-
static BOOL isFaceIDDevice = NO;
|
|
180
|
-
|
|
181
|
-
static dispatch_once_t onceToken;
|
|
182
|
-
|
|
183
|
-
dispatch_once(&onceToken, ^{
|
|
184
|
-
LAContext *context = [LAContext new];
|
|
185
|
-
[context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil];
|
|
186
|
-
isFaceIDDevice = context.biometryType == LABiometryTypeFaceID;
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
return isFaceIDDevice;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
+ (BOOL)isTouchIdDevice
|
|
193
|
-
{
|
|
194
|
-
static BOOL isTouchIDDevice = NO;
|
|
195
|
-
static dispatch_once_t onceToken;
|
|
196
|
-
|
|
197
|
-
dispatch_once(&onceToken, ^{
|
|
198
|
-
LAContext *context = [LAContext new];
|
|
199
|
-
[context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil];
|
|
200
|
-
isTouchIDDevice = context.biometryType == LABiometryTypeTouchID;
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return isTouchIDDevice;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@end
|
package/unimodule.json
DELETED