expo-local-authentication 11.1.1 → 12.1.1

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 CHANGED
@@ -10,10 +10,33 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
- ## 11.1.1 — 2021-06-24
13
+ ## 12.1.1 — 2022-02-01
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fix `Plugin with id 'maven' not found` build error from Android Gradle 7. ([#16080](https://github.com/expo/expo/pull/16080) by [@kudo](https://github.com/kudo))
18
+
19
+ ## 12.1.0 — 2021-12-03
14
20
 
15
21
  _This version does not introduce any user-facing changes._
16
22
 
23
+ ## 12.0.0 — 2021-09-28
24
+
25
+ ### 🛠 Breaking changes
26
+
27
+ - Dropped support for iOS 11.0 ([#14383](https://github.com/expo/expo/pull/14383) by [@cruzach](https://github.com/cruzach))
28
+
29
+ ### 🐛 Bug fixes
30
+
31
+ - Added missing definition on type LocalAuthenticationResult. ([#13636](https://github.com/expo/expo/pull/13636) by [@mstach60161](https://github.com/mstach60161))
32
+ - Fixed detection of the available authentication types on some Samsung devices on Android. ([#14300](https://github.com/expo/expo/pull/14300) by [@beaur](https://github.com/beaur))
33
+ - Fix building errors from use_frameworks! in Podfile. ([#14523](https://github.com/expo/expo/pull/14523) by [@kudo](https://github.com/kudo))
34
+
35
+ ### 💡 Others
36
+
37
+ - Rewrite module from Java to Kotlin. ([#13582](https://github.com/expo/expo/pull/13582) by [@mstach60161](https://github.com/mstach60161))
38
+ - Updated `@expo/config-plugins` ([#14443](https://github.com/expo/expo/pull/14443) by [@EvanBacon](https://github.com/EvanBacon))
39
+
17
40
  ## 11.1.0 — 2021-06-16
18
41
 
19
42
  ### 🐛 Bug fixes
package/README.md CHANGED
@@ -13,7 +13,7 @@ For [managed](https://docs.expo.io/versions/latest/introduction/managed-vs-bare/
13
13
 
14
14
  # Installation in bare React Native projects
15
15
 
16
- For bare React Native projects, you must ensure that you have [installed and configured the `react-native-unimodules` package](https://github.com/expo/expo/tree/master/packages/react-native-unimodules) before continuing.
16
+ For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
17
17
 
18
18
  ### Add the package to your npm dependencies
19
19
 
@@ -1,9 +1,9 @@
1
1
  apply plugin: 'com.android.library'
2
2
  apply plugin: 'kotlin-android'
3
- apply plugin: 'maven'
3
+ apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '11.1.1'
6
+ version = '12.1.1'
7
7
 
8
8
  buildscript {
9
9
  // Simple helper that allows the root project to override versions declared by this library.
@@ -20,27 +20,25 @@ buildscript {
20
20
  }
21
21
  }
22
22
 
23
- // Upload android library to maven with javadoc and android sources
24
- configurations {
25
- deployerJars
26
- }
27
-
28
23
  // Creating sources with comments
29
24
  task androidSourcesJar(type: Jar) {
30
25
  classifier = 'sources'
31
26
  from android.sourceSets.main.java.srcDirs
32
27
  }
33
28
 
34
- // Put the androidSources and javadoc to the artifacts
35
- artifacts {
36
- archives androidSourcesJar
37
- }
38
-
39
- uploadArchives {
40
- repositories {
41
- mavenDeployer {
42
- configuration = configurations.deployerJars
43
- repository(url: mavenLocal().url)
29
+ afterEvaluate {
30
+ publishing {
31
+ publications {
32
+ release(MavenPublication) {
33
+ from components.release
34
+ // Add additional sourcesJar to artifacts
35
+ artifact(androidSourcesJar)
36
+ }
37
+ }
38
+ repositories {
39
+ maven {
40
+ url = mavenLocal().url
41
+ }
44
42
  }
45
43
  }
46
44
  }
@@ -53,27 +51,23 @@ android {
53
51
  targetCompatibility JavaVersion.VERSION_1_8
54
52
  }
55
53
 
54
+ kotlinOptions {
55
+ jvmTarget = JavaVersion.VERSION_1_8
56
+ }
57
+
56
58
  defaultConfig {
57
59
  minSdkVersion safeExtGet("minSdkVersion", 21)
58
60
  targetSdkVersion safeExtGet("targetSdkVersion", 30)
59
61
  versionCode 30
60
- versionName "11.1.1"
62
+ versionName "12.1.1"
61
63
  }
62
64
  lintOptions {
63
65
  abortOnError false
64
66
  }
65
67
  }
66
68
 
67
- if (new File(rootProject.projectDir.parentFile, 'package.json').exists()) {
68
- apply from: project(":unimodules-core").file("../unimodules-core.gradle")
69
- } else {
70
- throw new GradleException(
71
- "'unimodules-core.gradle' was not found in the usual React Native dependency location. " +
72
- "This package can only be used in such projects. Are you sure you've installed the dependencies properly?")
73
- }
74
-
75
69
  dependencies {
76
- unimodule "unimodules-core"
70
+ implementation project(':expo-modules-core')
77
71
 
78
72
  api "androidx.biometric:biometric:1.1.0"
79
73
 
@@ -0,0 +1,288 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+ package expo.modules.localauthentication
3
+
4
+ import android.app.Activity
5
+ import android.app.KeyguardManager
6
+ import android.content.Context
7
+ import android.content.Intent
8
+ import android.os.Build
9
+ import android.os.Bundle
10
+ import androidx.biometric.BiometricManager
11
+ import androidx.biometric.BiometricPrompt
12
+ import androidx.biometric.BiometricPrompt.PromptInfo
13
+ import androidx.fragment.app.FragmentActivity
14
+ import expo.modules.core.ExportedModule
15
+ import expo.modules.core.ModuleRegistry
16
+ import expo.modules.core.ModuleRegistryDelegate
17
+ import expo.modules.core.Promise
18
+ import expo.modules.core.interfaces.ActivityEventListener
19
+ import expo.modules.core.interfaces.ActivityProvider
20
+ import expo.modules.core.interfaces.ExpoMethod
21
+ import expo.modules.core.interfaces.services.UIManager
22
+ import java.util.*
23
+ import java.util.concurrent.Executor
24
+ import java.util.concurrent.Executors
25
+
26
+ class LocalAuthenticationModule(context: Context) : ExportedModule(context), ActivityEventListener {
27
+ private val AUTHENTICATION_TYPE_FINGERPRINT = 1
28
+ private val AUTHENTICATION_TYPE_FACIAL_RECOGNITION = 2
29
+ private val AUTHENTICATION_TYPE_IRIS = 3
30
+ private val SECURITY_LEVEL_NONE = 0
31
+ private val SECURITY_LEVEL_SECRET = 1
32
+ private val SECURITY_LEVEL_BIOMETRIC = 2
33
+ private val biometricManager = BiometricManager.from(context)
34
+ private val packageManager = context.packageManager
35
+ private var biometricPrompt: BiometricPrompt? = null
36
+ private var promise: Promise? = null
37
+ private var isAuthenticating = false
38
+ private val moduleRegistryDelegate: ModuleRegistryDelegate = ModuleRegistryDelegate()
39
+ private val uIManager: UIManager by moduleRegistry()
40
+
41
+ private inline fun <reified T> moduleRegistry() = moduleRegistryDelegate.getFromModuleRegistry<T>()
42
+
43
+ private fun convertErrorCode(code: Int): String {
44
+ return when (code) {
45
+ BiometricPrompt.ERROR_CANCELED, BiometricPrompt.ERROR_NEGATIVE_BUTTON, BiometricPrompt.ERROR_USER_CANCELED -> "user_cancel"
46
+ BiometricPrompt.ERROR_HW_NOT_PRESENT, BiometricPrompt.ERROR_HW_UNAVAILABLE, BiometricPrompt.ERROR_NO_BIOMETRICS, BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL -> "not_available"
47
+ BiometricPrompt.ERROR_LOCKOUT, BiometricPrompt.ERROR_LOCKOUT_PERMANENT -> "lockout"
48
+ BiometricPrompt.ERROR_NO_SPACE -> "no_space"
49
+ BiometricPrompt.ERROR_TIMEOUT -> "timeout"
50
+ BiometricPrompt.ERROR_UNABLE_TO_PROCESS -> "unable_to_process"
51
+ else -> "unknown"
52
+ }
53
+ }
54
+
55
+ private val authenticationCallback: BiometricPrompt.AuthenticationCallback = object : BiometricPrompt.AuthenticationCallback() {
56
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
57
+ isAuthenticating = false
58
+ biometricPrompt = null
59
+ promise?.resolve(
60
+ Bundle().apply {
61
+ putBoolean("success", true)
62
+ }
63
+ )
64
+ promise = null
65
+ }
66
+
67
+ override fun onAuthenticationError(errMsgId: Int, errString: CharSequence) {
68
+ isAuthenticating = false
69
+ biometricPrompt = null
70
+ promise?.resolve(
71
+ Bundle().apply {
72
+ putBoolean("success", false)
73
+ putString("error", convertErrorCode(errMsgId))
74
+ putString("warning", errString.toString())
75
+ }
76
+ )
77
+ promise = null
78
+ }
79
+ }
80
+
81
+ override fun getName(): String {
82
+ return "ExpoLocalAuthentication"
83
+ }
84
+
85
+ override fun onCreate(moduleRegistry: ModuleRegistry) {
86
+ moduleRegistryDelegate.onCreate(moduleRegistry)
87
+ uIManager.registerActivityEventListener(this)
88
+ }
89
+
90
+ @ExpoMethod
91
+ fun supportedAuthenticationTypesAsync(promise: Promise) {
92
+ val result = biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)
93
+ val results: MutableList<Int> = ArrayList()
94
+ if (result == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
95
+ promise.resolve(results)
96
+ return
97
+ }
98
+
99
+ // note(cedric): replace hardcoded system feature strings with constants from
100
+ // PackageManager when dropping support for Android SDK 28
101
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
102
+ if (packageManager.hasSystemFeature("android.hardware.fingerprint")) {
103
+ results.add(AUTHENTICATION_TYPE_FINGERPRINT)
104
+ }
105
+ }
106
+ if (Build.VERSION.SDK_INT >= 29) {
107
+ if (packageManager.hasSystemFeature("android.hardware.biometrics.face")) {
108
+ results.add(AUTHENTICATION_TYPE_FACIAL_RECOGNITION)
109
+ }
110
+ if (packageManager.hasSystemFeature("android.hardware.biometrics.iris")) {
111
+ results.add(AUTHENTICATION_TYPE_IRIS)
112
+ }
113
+ }
114
+
115
+ // check for face recognition support on some samsung devices
116
+ if (packageManager.hasSystemFeature("com.samsung.android.bio.face") && !results.contains(AUTHENTICATION_TYPE_FACIAL_RECOGNITION)) {
117
+ results.add(AUTHENTICATION_TYPE_FACIAL_RECOGNITION)
118
+ }
119
+
120
+ promise.resolve(results)
121
+ }
122
+
123
+ @ExpoMethod
124
+ fun hasHardwareAsync(promise: Promise) {
125
+ val result = biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)
126
+ promise.resolve(result != BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE)
127
+ }
128
+
129
+ @ExpoMethod
130
+ fun isEnrolledAsync(promise: Promise) {
131
+ val result = biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)
132
+ promise.resolve(result == BiometricManager.BIOMETRIC_SUCCESS)
133
+ }
134
+
135
+ @ExpoMethod
136
+ fun getEnrolledLevelAsync(promise: Promise) {
137
+ var level = SECURITY_LEVEL_NONE
138
+ if (isDeviceSecure) {
139
+ level = SECURITY_LEVEL_SECRET
140
+ }
141
+ val result = biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)
142
+ if (result == BiometricManager.BIOMETRIC_SUCCESS) {
143
+ level = SECURITY_LEVEL_BIOMETRIC
144
+ }
145
+ promise.resolve(level)
146
+ }
147
+
148
+ @ExpoMethod
149
+ fun authenticateAsync(options: Map<String?, Any?>, promise: Promise) {
150
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
151
+ promise.reject("E_NOT_SUPPORTED", "Cannot display biometric prompt on android versions below 6.0")
152
+ return
153
+ }
154
+ if (currentActivity == null) {
155
+ promise.reject("E_NOT_FOREGROUND", "Cannot display biometric prompt when the app is not in the foreground")
156
+ return
157
+ }
158
+ if (!keyguardManager.isDeviceSecure) {
159
+ promise.resolve(
160
+ Bundle().apply {
161
+ putBoolean("success", false)
162
+ putString("error", "not_enrolled")
163
+ putString("warning", "KeyguardManager#isDeviceSecure() returned false")
164
+ }
165
+ )
166
+ return
167
+ }
168
+ val fragmentActivity = currentActivity as FragmentActivity?
169
+ if (fragmentActivity == null) {
170
+ promise.resolve(
171
+ Bundle().apply {
172
+ putBoolean("success", false)
173
+ putString("error", "not_available")
174
+ putString("warning", "getCurrentActivity() returned null")
175
+ }
176
+ )
177
+ return
178
+ }
179
+
180
+ // BiometricPrompt callbacks are invoked on the main thread so also run this there to avoid
181
+ // having to do locking.
182
+ uIManager.runOnUiQueueThread(
183
+ Runnable {
184
+ if (isAuthenticating) {
185
+ this.promise?.resolve(
186
+ Bundle().apply {
187
+ putBoolean("success", false)
188
+ putString("error", "app_cancel")
189
+ }
190
+ )
191
+ this.promise = promise
192
+ return@Runnable
193
+ }
194
+ val promptMessage = if (options.containsKey("promptMessage")) {
195
+ options["promptMessage"] as String?
196
+ } else {
197
+ ""
198
+ }
199
+ val cancelLabel = if (options.containsKey("cancelLabel")) {
200
+ options["cancelLabel"] as String?
201
+ } else {
202
+ ""
203
+ }
204
+ val disableDeviceFallback = if (options.containsKey("disableDeviceFallback")) {
205
+ options["disableDeviceFallback"] as Boolean?
206
+ } else {
207
+ false
208
+ }
209
+ isAuthenticating = true
210
+ this.promise = promise
211
+ val executor: Executor = Executors.newSingleThreadExecutor()
212
+ biometricPrompt = BiometricPrompt(fragmentActivity, executor, authenticationCallback)
213
+ val promptInfoBuilder = PromptInfo.Builder()
214
+ promptMessage?.let {
215
+ promptInfoBuilder.setTitle(it)
216
+ }
217
+ if (disableDeviceFallback == true) {
218
+ cancelLabel?.let {
219
+ promptInfoBuilder.setNegativeButtonText(it)
220
+ }
221
+ } else {
222
+ promptInfoBuilder.setAllowedAuthenticators(
223
+ BiometricManager.Authenticators.BIOMETRIC_WEAK
224
+ or BiometricManager.Authenticators.DEVICE_CREDENTIAL
225
+ )
226
+ }
227
+ val promptInfo = promptInfoBuilder.build()
228
+ try {
229
+ biometricPrompt!!.authenticate(promptInfo)
230
+ } catch (ex: NullPointerException) {
231
+ promise.reject("E_INTERNAL_ERRROR", "Canceled authentication due to an internal error")
232
+ }
233
+ }
234
+ )
235
+ }
236
+
237
+ @ExpoMethod
238
+ fun cancelAuthenticate(promise: Promise) {
239
+ uIManager.runOnUiQueueThread {
240
+ biometricPrompt?.cancelAuthentication()
241
+ isAuthenticating = false
242
+ promise.resolve(null)
243
+ }
244
+ }
245
+
246
+ override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
247
+ // If the user uses PIN as an authentication method, the result will be passed to the `onActivityResult`.
248
+ // Unfortunately, react-native doesn't pass this value to the underlying fragment - we won't resolve the promise.
249
+ // So we need to do it manually.
250
+ if (activity is FragmentActivity) {
251
+ val fragment = activity.supportFragmentManager.findFragmentByTag("androidx.biometric.BiometricFragment")
252
+ fragment?.onActivityResult(requestCode and 0xffff, resultCode, data)
253
+ }
254
+ }
255
+
256
+ override fun onNewIntent(intent: Intent) = Unit
257
+
258
+ // NOTE: `KeyguardManager#isKeyguardSecure()` considers SIM locked state,
259
+ // but it will be ignored on falling-back to device credential on biometric authentication.
260
+ // That means, setting level to `SECURITY_LEVEL_SECRET` might be misleading for some users.
261
+ // But there is no equivalent APIs prior to M.
262
+ // `andriodx.biometric.BiometricManager#canAuthenticate(int)` looks like an alternative,
263
+ // but specifying `BiometricManager.Authenticators.DEVICE_CREDENTIAL` alone is not
264
+ // supported prior to API 30.
265
+ // https://developer.android.com/reference/androidx/biometric/BiometricManager#canAuthenticate(int)
266
+ private val isDeviceSecure: Boolean
267
+ get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
268
+ keyguardManager.isDeviceSecure
269
+ } else {
270
+ // NOTE: `KeyguardManager#isKeyguardSecure()` considers SIM locked state,
271
+ // but it will be ignored on falling-back to device credential on biometric authentication.
272
+ // That means, setting level to `SECURITY_LEVEL_SECRET` might be misleading for some users.
273
+ // But there is no equivalent APIs prior to M.
274
+ // `andriodx.biometric.BiometricManager#canAuthenticate(int)` looks like an alternative,
275
+ // but specifying `BiometricManager.Authenticators.DEVICE_CREDENTIAL` alone is not
276
+ // supported prior to API 30.
277
+ // https://developer.android.com/reference/androidx/biometric/BiometricManager#canAuthenticate(int)
278
+ keyguardManager.isKeyguardSecure
279
+ }
280
+
281
+ private val keyguardManager: KeyguardManager
282
+ get() = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
283
+ private val currentActivity: Activity?
284
+ get() {
285
+ val activityProvider: ActivityProvider by moduleRegistry()
286
+ return activityProvider.currentActivity
287
+ }
288
+ }
@@ -0,0 +1,11 @@
1
+ package expo.modules.localauthentication
2
+
3
+ import android.content.Context
4
+ import expo.modules.core.BasePackage
5
+ import expo.modules.core.ExportedModule
6
+
7
+ class LocalAuthenticationPackage : BasePackage() {
8
+ override fun createExportedModules(context: Context): List<ExportedModule> {
9
+ return listOf<ExportedModule>(LocalAuthenticationModule(context))
10
+ }
11
+ }
@@ -1,2 +1,2 @@
1
- declare const _default: import("@unimodules/core").ProxyNativeModule;
1
+ declare const _default: import("expo-modules-core").ProxyNativeModule;
2
2
  export default _default;
@@ -1,3 +1,3 @@
1
- import { NativeModulesProxy } from '@unimodules/core';
1
+ import { NativeModulesProxy } from 'expo-modules-core';
2
2
  export default NativeModulesProxy.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,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAe,kBAAkB,CAAC,uBAAuB,CAAC","sourcesContent":["import { NativeModulesProxy } from '@unimodules/core';\n\nexport default NativeModulesProxy.ExpoLocalAuthentication;\n"]}
1
+ {"version":3,"file":"ExpoLocalAuthentication.js","sourceRoot":"","sources":["../src/ExpoLocalAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,eAAe,kBAAkB,CAAC,uBAAuB,CAAC","sourcesContent":["import { NativeModulesProxy } from 'expo-modules-core';\n\nexport default NativeModulesProxy.ExpoLocalAuthentication;\n"]}
@@ -1,8 +1,44 @@
1
1
  import { LocalAuthenticationOptions, AuthenticationType, LocalAuthenticationResult, SecurityLevel } from './LocalAuthentication.types';
2
2
  export { LocalAuthenticationOptions, AuthenticationType, LocalAuthenticationResult, SecurityLevel };
3
+ /**
4
+ * Determine whether a face or fingerprint scanner is available on the device.
5
+ * @return Returns a promise which fulfils with a `boolean` value indicating whether a face or
6
+ * fingerprint scanner is available on this device.
7
+ */
3
8
  export declare function hasHardwareAsync(): Promise<boolean>;
9
+ /**
10
+ * Determine what kinds of authentications are available on the device.
11
+ * @return Returns a promise which fulfils to an array containing [`AuthenticationType`s](#authenticationtype).
12
+ *
13
+ * Devices can support multiple authentication methods- i.e. `[1,2]` means the device supports both
14
+ * fingerprint and facial recognition. If none are supported, this method returns an empty array.
15
+ */
4
16
  export declare function supportedAuthenticationTypesAsync(): Promise<AuthenticationType[]>;
17
+ /**
18
+ * Determine whether the device has saved fingerprints or facial data to use for authentication.
19
+ * @return Returns a promise which fulfils to `boolean` value indicating whether the device has
20
+ * saved fingerprints or facial data for authentication.
21
+ */
5
22
  export declare function isEnrolledAsync(): Promise<boolean>;
23
+ /**
24
+ * Determine what kind of authentication is enrolled on the device.
25
+ * @return Returns a promise which fulfils with [`SecurityLevel`](#securitylevel).
26
+ * > **Note:** On Android devices prior to M, `SECRET` can be returned if only the SIM lock has been
27
+ * enrolled, which is not the method that [`authenticateAsync`](#localauthenticationauthenticateasyncoptions)
28
+ * prompts.
29
+ */
6
30
  export declare function getEnrolledLevelAsync(): Promise<SecurityLevel>;
31
+ /**
32
+ * Attempts to authenticate via Fingerprint/TouchID (or FaceID if available on the device).
33
+ * > **Note:** Apple requires apps which use FaceID to provide a description of why they use this API.
34
+ * If you try to use FaceID on an iPhone with FaceID without providing `infoPlist.NSFaceIDUsageDescription`
35
+ * in `app.json`, the module will authenticate using device passcode. For more information about
36
+ * usage descriptions on iOS, see [Deploying to App Stores](/distribution/app-stores#system-permissions-dialogs-on-ios).
37
+ * @param options
38
+ * @return Returns a promise which fulfils with [`LocalAuthenticationResult`](#localauthenticationresult).
39
+ */
7
40
  export declare function authenticateAsync(options?: LocalAuthenticationOptions): Promise<LocalAuthenticationResult>;
41
+ /**
42
+ * **(Android Only)** Cancels authentication flow.
43
+ */
8
44
  export declare function cancelAuthenticate(): Promise<void>;
@@ -1,32 +1,70 @@
1
- import { UnavailabilityError } from '@unimodules/core';
1
+ import { UnavailabilityError } from 'expo-modules-core';
2
2
  import invariant from 'invariant';
3
3
  import ExpoLocalAuthentication from './ExpoLocalAuthentication';
4
4
  import { AuthenticationType, SecurityLevel, } from './LocalAuthentication.types';
5
5
  export { AuthenticationType, SecurityLevel };
6
+ // @needsAudit
7
+ /**
8
+ * Determine whether a face or fingerprint scanner is available on the device.
9
+ * @return Returns a promise which fulfils with a `boolean` value indicating whether a face or
10
+ * fingerprint scanner is available on this device.
11
+ */
6
12
  export async function hasHardwareAsync() {
7
13
  if (!ExpoLocalAuthentication.hasHardwareAsync) {
8
14
  throw new UnavailabilityError('expo-local-authentication', 'hasHardwareAsync');
9
15
  }
10
16
  return await ExpoLocalAuthentication.hasHardwareAsync();
11
17
  }
18
+ // @needsAudit
19
+ /**
20
+ * Determine what kinds of authentications are available on the device.
21
+ * @return Returns a promise which fulfils to an array containing [`AuthenticationType`s](#authenticationtype).
22
+ *
23
+ * Devices can support multiple authentication methods- i.e. `[1,2]` means the device supports both
24
+ * fingerprint and facial recognition. If none are supported, this method returns an empty array.
25
+ */
12
26
  export async function supportedAuthenticationTypesAsync() {
13
27
  if (!ExpoLocalAuthentication.supportedAuthenticationTypesAsync) {
14
28
  throw new UnavailabilityError('expo-local-authentication', 'supportedAuthenticationTypesAsync');
15
29
  }
16
30
  return await ExpoLocalAuthentication.supportedAuthenticationTypesAsync();
17
31
  }
32
+ // @needsAudit
33
+ /**
34
+ * Determine whether the device has saved fingerprints or facial data to use for authentication.
35
+ * @return Returns a promise which fulfils to `boolean` value indicating whether the device has
36
+ * saved fingerprints or facial data for authentication.
37
+ */
18
38
  export async function isEnrolledAsync() {
19
39
  if (!ExpoLocalAuthentication.isEnrolledAsync) {
20
40
  throw new UnavailabilityError('expo-local-authentication', 'isEnrolledAsync');
21
41
  }
22
42
  return await ExpoLocalAuthentication.isEnrolledAsync();
23
43
  }
44
+ // @needsAudit
45
+ /**
46
+ * Determine what kind of authentication is enrolled on the device.
47
+ * @return Returns a promise which fulfils with [`SecurityLevel`](#securitylevel).
48
+ * > **Note:** On Android devices prior to M, `SECRET` can be returned if only the SIM lock has been
49
+ * enrolled, which is not the method that [`authenticateAsync`](#localauthenticationauthenticateasyncoptions)
50
+ * prompts.
51
+ */
24
52
  export async function getEnrolledLevelAsync() {
25
53
  if (!ExpoLocalAuthentication.getEnrolledLevelAsync) {
26
54
  throw new UnavailabilityError('expo-local-authentication', 'getEnrolledLevelAsync');
27
55
  }
28
56
  return await ExpoLocalAuthentication.getEnrolledLevelAsync();
29
57
  }
58
+ // @needsAudit
59
+ /**
60
+ * Attempts to authenticate via Fingerprint/TouchID (or FaceID if available on the device).
61
+ * > **Note:** Apple requires apps which use FaceID to provide a description of why they use this API.
62
+ * If you try to use FaceID on an iPhone with FaceID without providing `infoPlist.NSFaceIDUsageDescription`
63
+ * in `app.json`, the module will authenticate using device passcode. For more information about
64
+ * usage descriptions on iOS, see [Deploying to App Stores](/distribution/app-stores#system-permissions-dialogs-on-ios).
65
+ * @param options
66
+ * @return Returns a promise which fulfils with [`LocalAuthenticationResult`](#localauthenticationresult).
67
+ */
30
68
  export async function authenticateAsync(options = {}) {
31
69
  if (!ExpoLocalAuthentication.authenticateAsync) {
32
70
  throw new UnavailabilityError('expo-local-authentication', 'authenticateAsync');
@@ -41,6 +79,10 @@ export async function authenticateAsync(options = {}) {
41
79
  }
42
80
  return result;
43
81
  }
82
+ // @needsAudit
83
+ /**
84
+ * **(Android Only)** Cancels authentication flow.
85
+ */
44
86
  export async function cancelAuthenticate() {
45
87
  if (!ExpoLocalAuthentication.cancelAuthenticate) {
46
88
  throw new UnavailabilityError('expo-local-authentication', 'cancelAuthenticate');
@@ -1 +1 @@
1
- {"version":3,"file":"LocalAuthentication.js","sourceRoot":"","sources":["../src/LocalAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,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,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,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,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,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,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,IAAI,MAAM,CAAC,OAAO,EAAE;QAClB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;KAC9B;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,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 '@unimodules/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\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\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\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\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\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 if (result.warning) {\n console.warn(result.warning);\n }\n return result;\n}\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
+ {"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,IAAI,MAAM,CAAC,OAAO,EAAE;QAClB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;KAC9B;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,cAAc;AACd;;GAEG;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 [Deploying to App Stores](/distribution/app-stores#system-permissions-dialogs-on-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 if (result.warning) {\n console.warn(result.warning);\n }\n return result;\n}\n\n// @needsAudit\n/**\n * **(Android Only)** Cancels authentication flow.\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"]}
@@ -3,20 +3,60 @@ export declare type LocalAuthenticationResult = {
3
3
  } | {
4
4
  success: false;
5
5
  error: string;
6
+ warning?: string;
6
7
  };
7
8
  export declare enum AuthenticationType {
9
+ /**
10
+ * Indicates fingerprint support.
11
+ */
8
12
  FINGERPRINT = 1,
13
+ /**
14
+ * Indicates facial recognition support.
15
+ */
9
16
  FACIAL_RECOGNITION = 2,
17
+ /**
18
+ * Indicates iris recognition support.
19
+ * @platform android
20
+ */
10
21
  IRIS = 3
11
22
  }
12
23
  export declare enum SecurityLevel {
24
+ /**
25
+ * Indicates no enrolled authentication.
26
+ */
13
27
  NONE = 0,
28
+ /**
29
+ * Indicates non-biometric authentication (e.g. PIN, Pattern).
30
+ */
14
31
  SECRET = 1,
32
+ /**
33
+ * Indicates biometric authentication.
34
+ */
15
35
  BIOMETRIC = 2
16
36
  }
17
37
  export declare type LocalAuthenticationOptions = {
38
+ /**
39
+ * A message that is shown alongside the TouchID or FaceID prompt.
40
+ */
18
41
  promptMessage?: string;
42
+ /**
43
+ * Allows to customize the default `Cancel` label shown.
44
+ */
19
45
  cancelLabel?: string;
46
+ /**
47
+ * After several failed attempts the system will fallback to the device passcode. This setting
48
+ * allows you to disable this option and instead handle the fallback yourself. This can be
49
+ * preferable in certain custom authentication workflows. This behaviour maps to using the iOS
50
+ * [LAPolicyDeviceOwnerAuthenticationWithBiometrics](https://developer.apple.com/documentation/localauthentication/lapolicy/lapolicydeviceownerauthenticationwithbiometrics?language=objc)
51
+ * policy rather than the [LAPolicyDeviceOwnerAuthentication](https://developer.apple.com/documentation/localauthentication/lapolicy/lapolicydeviceownerauthentication?language=objc)
52
+ * policy. Defaults to `false`.
53
+ */
20
54
  disableDeviceFallback?: boolean;
55
+ /**
56
+ * Allows to customize the default `Use Passcode` label shown after several failed
57
+ * authentication attempts. Setting this option to an empty string disables this button from
58
+ * showing in the prompt.
59
+ * @platform ios
60
+ */
21
61
  fallbackLabel?: string;
22
62
  };