expo-local-authentication 13.0.1 → 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 +14 -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.d.ts.map +1 -1
- package/build/LocalAuthentication.js +0 -3
- package/build/LocalAuthentication.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/src/LocalAuthentication.ts +0 -3
- 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,16 @@
|
|
|
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
|
+
|
|
19
|
+
## 13.0.2 — 2022-11-02
|
|
20
|
+
|
|
21
|
+
_This version does not introduce any user-facing changes._
|
|
22
|
+
|
|
13
23
|
## 13.0.1 — 2022-10-28
|
|
14
24
|
|
|
15
25
|
_This version does not introduce any user-facing changes._
|
|
@@ -166,3 +176,7 @@ _This version does not introduce any user-facing changes._
|
|
|
166
176
|
### 🐛 Bug fixes
|
|
167
177
|
|
|
168
178
|
- Added estimate of supported authentication types for Android. ([#8431](https://github.com/expo/expo/pull/8431) by [@bycedric](https://github.com/bycedric))
|
|
179
|
+
|
|
180
|
+
### 💡 Others
|
|
181
|
+
|
|
182
|
+
- Removed `console.warn` from the `authenticateAsync` function, preventing running it every time the user cancels authentication or authentication fails. ([#19803](https://github.com/expo/expo/pull/19803) by [@GustavoMont](https://github.com/GustavoMont))
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalAuthentication.d.ts","sourceRoot":"","sources":["../src/LocalAuthentication.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAClB,yBAAyB,EACzB,aAAa,EACd,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,aAAa,EAAE,CAAC;AAGpG;;;;GAIG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAKzD;AAGD;;;;;;GAMG;AACH,wBAAsB,iCAAiC,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAKvF;AAGD;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAKxD;AAGD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,aAAa,CAAC,CAKpE;AAGD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,yBAAyB,CAAC,
|
|
1
|
+
{"version":3,"file":"LocalAuthentication.d.ts","sourceRoot":"","sources":["../src/LocalAuthentication.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAClB,yBAAyB,EACzB,aAAa,EACd,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,aAAa,EAAE,CAAC;AAGpG;;;;GAIG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAKzD;AAGD;;;;;;GAMG;AACH,wBAAsB,iCAAiC,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAKvF;AAGD;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAKxD;AAGD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,aAAa,CAAC,CAKpE;AAGD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,yBAAyB,CAAC,CAgBpC;AAGD;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKxD"}
|
|
@@ -74,9 +74,6 @@ export async function authenticateAsync(options = {}) {
|
|
|
74
74
|
}
|
|
75
75
|
const promptMessage = options.promptMessage || 'Authenticate';
|
|
76
76
|
const result = await ExpoLocalAuthentication.authenticateAsync({ ...options, promptMessage });
|
|
77
|
-
if (result.warning) {
|
|
78
|
-
console.warn(result.warning);
|
|
79
|
-
}
|
|
80
77
|
return result;
|
|
81
78
|
}
|
|
82
79
|
// @needsAudit
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalAuthentication.js","sourceRoot":"","sources":["../src/LocalAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,uBAAuB,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAEL,kBAAkB,EAElB,aAAa,GACd,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAA8B,kBAAkB,EAA6B,aAAa,EAAE,CAAC;AAEpG,cAAc;AACd;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,EAAE;QAC7C,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,kBAAkB,CAAC,CAAC;KAChF;IACD,OAAO,MAAM,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;AAC1D,CAAC;AAED,cAAc;AACd;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC;IACrD,IAAI,CAAC,uBAAuB,CAAC,iCAAiC,EAAE;QAC9D,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,mCAAmC,CAAC,CAAC;KACjG;IACD,OAAO,MAAM,uBAAuB,CAAC,iCAAiC,EAAE,CAAC;AAC3E,CAAC;AAED,cAAc;AACd;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC,uBAAuB,CAAC,eAAe,EAAE;QAC5C,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,iBAAiB,CAAC,CAAC;KAC/E;IACD,OAAO,MAAM,uBAAuB,CAAC,eAAe,EAAE,CAAC;AACzD,CAAC;AAED,cAAc;AACd;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC,uBAAuB,CAAC,qBAAqB,EAAE;QAClD,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,uBAAuB,CAAC,CAAC;KACrF;IACD,OAAO,MAAM,uBAAuB,CAAC,qBAAqB,EAAE,CAAC;AAC/D,CAAC;AAED,cAAc;AACd;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAsC,EAAE;IAExC,IAAI,CAAC,uBAAuB,CAAC,iBAAiB,EAAE;QAC9C,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,mBAAmB,CAAC,CAAC;KACjF;IAED,IAAI,OAAO,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE;QAC3C,SAAS,CACP,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,EACzE,6FAA6F,CAC9F,CAAC;KACH;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,cAAc,CAAC;IAC9D,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC,iBAAiB,CAAC,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IAE9F,
|
|
1
|
+
{"version":3,"file":"LocalAuthentication.js","sourceRoot":"","sources":["../src/LocalAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,uBAAuB,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAEL,kBAAkB,EAElB,aAAa,GACd,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAA8B,kBAAkB,EAA6B,aAAa,EAAE,CAAC;AAEpG,cAAc;AACd;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,EAAE;QAC7C,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,kBAAkB,CAAC,CAAC;KAChF;IACD,OAAO,MAAM,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;AAC1D,CAAC;AAED,cAAc;AACd;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC;IACrD,IAAI,CAAC,uBAAuB,CAAC,iCAAiC,EAAE;QAC9D,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,mCAAmC,CAAC,CAAC;KACjG;IACD,OAAO,MAAM,uBAAuB,CAAC,iCAAiC,EAAE,CAAC;AAC3E,CAAC;AAED,cAAc;AACd;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC,uBAAuB,CAAC,eAAe,EAAE;QAC5C,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,iBAAiB,CAAC,CAAC;KAC/E;IACD,OAAO,MAAM,uBAAuB,CAAC,eAAe,EAAE,CAAC;AACzD,CAAC;AAED,cAAc;AACd;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC,uBAAuB,CAAC,qBAAqB,EAAE;QAClD,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,uBAAuB,CAAC,CAAC;KACrF;IACD,OAAO,MAAM,uBAAuB,CAAC,qBAAqB,EAAE,CAAC;AAC/D,CAAC;AAED,cAAc;AACd;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAsC,EAAE;IAExC,IAAI,CAAC,uBAAuB,CAAC,iBAAiB,EAAE;QAC9C,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,mBAAmB,CAAC,CAAC;KACjF;IAED,IAAI,OAAO,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE;QAC3C,SAAS,CACP,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,EACzE,6FAA6F,CAC9F,CAAC;KACH;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,cAAc,CAAC;IAC9D,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC,iBAAiB,CAAC,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IAE9F,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,cAAc;AACd;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC,uBAAuB,CAAC,kBAAkB,EAAE;QAC/C,MAAM,IAAI,mBAAmB,CAAC,2BAA2B,EAAE,oBAAoB,CAAC,CAAC;KAClF;IACD,MAAM,uBAAuB,CAAC,kBAAkB,EAAE,CAAC;AACrD,CAAC","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\nimport invariant from 'invariant';\n\nimport ExpoLocalAuthentication from './ExpoLocalAuthentication';\nimport {\n LocalAuthenticationOptions,\n AuthenticationType,\n LocalAuthenticationResult,\n SecurityLevel,\n} from './LocalAuthentication.types';\n\nexport { LocalAuthenticationOptions, AuthenticationType, LocalAuthenticationResult, SecurityLevel };\n\n// @needsAudit\n/**\n * Determine whether a face or fingerprint scanner is available on the device.\n * @return Returns a promise which fulfils with a `boolean` value indicating whether a face or\n * fingerprint scanner is available on this device.\n */\nexport async function hasHardwareAsync(): Promise<boolean> {\n if (!ExpoLocalAuthentication.hasHardwareAsync) {\n throw new UnavailabilityError('expo-local-authentication', 'hasHardwareAsync');\n }\n return await ExpoLocalAuthentication.hasHardwareAsync();\n}\n\n// @needsAudit\n/**\n * Determine what kinds of authentications are available on the device.\n * @return Returns a promise which fulfils to an array containing [`AuthenticationType`s](#authenticationtype).\n *\n * Devices can support multiple authentication methods- i.e. `[1,2]` means the device supports both\n * fingerprint and facial recognition. If none are supported, this method returns an empty array.\n */\nexport async function supportedAuthenticationTypesAsync(): Promise<AuthenticationType[]> {\n if (!ExpoLocalAuthentication.supportedAuthenticationTypesAsync) {\n throw new UnavailabilityError('expo-local-authentication', 'supportedAuthenticationTypesAsync');\n }\n return await ExpoLocalAuthentication.supportedAuthenticationTypesAsync();\n}\n\n// @needsAudit\n/**\n * Determine whether the device has saved fingerprints or facial data to use for authentication.\n * @return Returns a promise which fulfils to `boolean` value indicating whether the device has\n * saved fingerprints or facial data for authentication.\n */\nexport async function isEnrolledAsync(): Promise<boolean> {\n if (!ExpoLocalAuthentication.isEnrolledAsync) {\n throw new UnavailabilityError('expo-local-authentication', 'isEnrolledAsync');\n }\n return await ExpoLocalAuthentication.isEnrolledAsync();\n}\n\n// @needsAudit\n/**\n * Determine what kind of authentication is enrolled on the device.\n * @return Returns a promise which fulfils with [`SecurityLevel`](#securitylevel).\n * > **Note:** On Android devices prior to M, `SECRET` can be returned if only the SIM lock has been\n * enrolled, which is not the method that [`authenticateAsync`](#localauthenticationauthenticateasyncoptions)\n * prompts.\n */\nexport async function getEnrolledLevelAsync(): Promise<SecurityLevel> {\n if (!ExpoLocalAuthentication.getEnrolledLevelAsync) {\n throw new UnavailabilityError('expo-local-authentication', 'getEnrolledLevelAsync');\n }\n return await ExpoLocalAuthentication.getEnrolledLevelAsync();\n}\n\n// @needsAudit\n/**\n * Attempts to authenticate via Fingerprint/TouchID (or FaceID if available on the device).\n * > **Note:** Apple requires apps which use FaceID to provide a description of why they use this API.\n * If you try to use FaceID on an iPhone with FaceID without providing `infoPlist.NSFaceIDUsageDescription`\n * in `app.json`, the module will authenticate using device passcode. For more information about\n * usage descriptions on iOS, see [permissions guide](/guides/permissions/#ios).\n * @param options\n * @return Returns a promise which fulfils with [`LocalAuthenticationResult`](#localauthenticationresult).\n */\nexport async function authenticateAsync(\n options: LocalAuthenticationOptions = {}\n): Promise<LocalAuthenticationResult> {\n if (!ExpoLocalAuthentication.authenticateAsync) {\n throw new UnavailabilityError('expo-local-authentication', 'authenticateAsync');\n }\n\n if (options.hasOwnProperty('promptMessage')) {\n invariant(\n typeof options.promptMessage === 'string' && options.promptMessage.length,\n 'LocalAuthentication.authenticateAsync : `options.promptMessage` must be a non-empty string.'\n );\n }\n\n const promptMessage = options.promptMessage || 'Authenticate';\n const result = await ExpoLocalAuthentication.authenticateAsync({ ...options, promptMessage });\n\n return result;\n}\n\n// @needsAudit\n/**\n * Cancels authentication flow.\n * @platform android\n */\nexport async function cancelAuthenticate(): Promise<void> {\n if (!ExpoLocalAuthentication.cancelAuthenticate) {\n throw new UnavailabilityError('expo-local-authentication', 'cancelAuthenticate');\n }\n await ExpoLocalAuthentication.cancelAuthenticate();\n}\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');
|
|
@@ -94,9 +94,6 @@ export async function authenticateAsync(
|
|
|
94
94
|
const promptMessage = options.promptMessage || 'Authenticate';
|
|
95
95
|
const result = await ExpoLocalAuthentication.authenticateAsync({ ...options, promptMessage });
|
|
96
96
|
|
|
97
|
-
if (result.warning) {
|
|
98
|
-
console.warn(result.warning);
|
|
99
|
-
}
|
|
100
97
|
return result;
|
|
101
98
|
}
|
|
102
99
|
|
|
@@ -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