@aparajita/capacitor-biometric-auth 8.0.2 → 9.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.
@@ -12,7 +12,7 @@
12
12
  s.author = package['author']
13
13
  s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
14
14
  s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
15
- s.ios.deployment_target = '13.0'
15
+ s.ios.deployment_target = '14.0'
16
16
  s.dependency 'Capacitor'
17
17
  s.swift_version = '5.1'
18
18
  end
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # capacitor-biometric-auth  [![npm version](https://badge.fury.io/js/@aparajita%2Fcapacitor-biometric-auth.svg)](https://badge.fury.io/js/@aparajita%2Fcapacitor-biometric-auth)
4
4
 
5
- This plugin for [Capacitor 6](https://capacitorjs.com) provides access to native biometry and device credentials on iOS and Android. It supports every type of biometry and every configuration option on both platforms. In addition, biometry and device credentials are simulated on the web so you can test your logic without making any changes to your code.
5
+ This plugin for [Capacitor 7](https://capacitorjs.com) provides access to native biometry and device credentials on iOS and Android. It supports every type of biometry and every configuration option on both platforms. In addition, biometry and device credentials are simulated on the web so you can test your logic without making any changes to your code.
6
6
 
7
7
  🛑 **BREAKING CHANGES:**
8
8
 
@@ -10,7 +10,7 @@ This plugin for [Capacitor 6](https://capacitorjs.com) provides access to native
10
10
 
11
11
  - If you are upgrading from a version prior to 7.0.0, please note that [`authenticate()`](#authenticate) will _immediately_ present a prompt for device credentials if `deviceIsSecure` is true, `allowDeviceCredentials` is true, and no biometry of the requested strength is available.
12
12
 
13
- - If you are upgrading from a version prior to 8.0.0, please note that this plugin now requires Capacitor 6+.
13
+ - If you are upgrading from a version prior to 8.0.0, please note that this plugin now requires Capacitor 7+.
14
14
 
15
15
  - If you are upgrading from a version prior to 8.0.0, please note that [`addResumeListener`](#addresumelistener) now always returns a Promise and must be awaited.
16
16
 
@@ -42,11 +42,23 @@ The API is extensively documented in the [TypeScript definitions file](src/defin
42
42
 
43
43
  Although not strictly necessary, before giving the user the option to use biometry (such as displaying a biometry icon), you will probably want to call [`checkBiometry()`](#checkbiometry) and inspect the [`CheckBiometryResult`](#checkbiometryresult) to see what (if any) biometry and/or device credentials are available on the device. Note the following:
44
44
 
45
- - `isAvailable` may be `false` but `biometryType` may indicate the presence of biometry on the device. This occurs if the current user is not enrolled in biometry, or if biometry has been disabled for the current app. In such cases the `reason` and `code` will tell you why.
45
+ - `isAvailable` may be `false` but `biometryType` may indicate the presence of biometry on the device. This occurs if:
46
+ - The current user is not enrolled in biometry.
47
+ - Biometry has been disabled for the current app.
48
+ - On Android, biometric hardware is present but can only be used for _device_ unlock, _not_ by apps. see the [Android note](#android-note) below for more information.
46
49
 
47
- - On iOS, `isAvailable` and `strongBiometryIsAvailable` will always have the same value. On Android, `isAvailable` will be `true` if _any_ type of biometry is available, but `strongBiometryIsAvailable` will be `true` only if strong biometry is available. For example, on a typical device, if the device supports both fingerprint and face authentication, `isAvailable` will be `true` if either is enrolled, but `strongBiometryIsAvailable` will be `true` only if fingerprint authentication is enrolled.
50
+ In such cases the `reason` and `code` will tell you why.
48
51
 
49
- - `biometryTypes` may contain more than one type of biometry. This occurs on Android devices that support multiple types of biometry. In such cases `biometryType` will indicate the primary (most secure) type of biometry, and the `biometryTypes` array will contain all of the biometry types supported by the device. Note that Android only guarantees that one of the types is actually available.
52
+ - On iOS, `isAvailable` and `strongBiometryIsAvailable` will always have the same value, because traditionally that’s the whole point of biometry it’s supposed to be strong authentication.
53
+ - Android unfortunately supports “strong” biometry (usually fingerprint) and “weak” biometry (usually face/iris). `isAvailable` will be `true` if _any_ type of biometry is actually available for use by the app, but `strongBiometryIsAvailable` will be `true` only if strong biometry is available. For example, on a typical device, if it has hardware support for both fingerprint and face unlock, and both are available for use by apps, `isAvailable` will be `true` if either is enrolled, but `strongBiometryIsAvailable` will be `true` only if fingerprint authentication is enrolled.
54
+
55
+ #### ❗Android note
56
+
57
+ When it comes to biometry, Android is frankly a bit of a mess. Some devices may support “weak” biometry (e.g. face unlock) only for device unlock, and not by apps. But the system reports to this plugin _all_ types of biometric hardware, even if they are not available for use by apps.
58
+
59
+ 👉 Therefore on Android the only thing you can rely on are the `isAvailable` and `strongBiometryAvailable` properties as simple on/off switches. Do NOT rely on `biometryType` and `biometryTypes` as an indication of what biometry you can actually use.
60
+
61
+ #### Handling app resume
50
62
 
51
63
  Because the availability of biometry can change while your app is in the background, it’s important to check availability when your app resumes. By calling [`addResumeListener()`](#addresumelistener) you can register a callback that is passed a [`CheckBiometryResult`](#checkbiometryresult) when your app resumes.
52
64
 
@@ -138,7 +150,9 @@ On iOS, Touch ID and Face ID are supported.
138
150
 
139
151
  ### Android
140
152
 
141
- On Android, fingerprint, face, and iris authentication are supported. Note that if a device supports more than one type of biometry, the plugin will initially present the primary (most secure) available type, which is determined by the system.
153
+ On Android, fingerprint, face, and iris authentication are supported. Note that if a device supports more than one type of biometry, the plugin will initially present the primary (most secure) available type, which is determined by the system and cannot be determined programmatically.
154
+
155
+ ❗ As discussed above, Android may support multiple types of biometry, but not all of them may be available for use by apps. For example, a device may support both fingerprint and face authentication, but only fingerprint authentication may be available for use by apps. In such cases, if the user has enrolled in face authentication only, `checkBiometry()` will indicate that biometry is not available.
142
156
 
143
157
  ## API
144
158
 
@@ -248,75 +262,17 @@ Register a function that will be called when the app resumes. The function will
248
262
 
249
263
  #### CheckBiometryResult
250
264
 
251
- | Prop | Type | Description |
252
- | :------------------------ | :------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
253
- | isAvailable | boolean | True if the device supports _at least_ weak biometric authentication and the current user has enrolled in some form of biometry. Note that if `strongBiometryIsAvailable` is true, this will also be true. |
254
- | strongBiometryIsAvailable | boolean | True if the device has strong biometric authentication capability and the current user has enrolled in that strong biometry.<br><br>On iOS this value and `isAvailable` will always be the same, since iOS only supports strong biometry.<br><br>On Android, for example, if the device supports both fingerprint and face authentication, and the user has enrolled only in face authentication, and Android considers face authentication on that device to be weak, then `isAvailable` will be true but this value will be false. |
255
- | biometryType | <a href="#biometrytype">BiometryType</a> | The primary (most secure) type of biometry supported by the device. Note that _supported_ is not the same as _available_, which requires the biometry to be enrolled. |
256
- | biometryTypes | BiometryType[] | All of the biometry types supported by the hardware on the device (currently only Android devices support multiple biometry types). If no biometry is supported, i.e. `biometryType === <a href="#biometrytype">BiometryType.none`</a>, this will be an empty array.<br><br>Note that _supported_ is not the same as _available_, which requires the biometry to be enrolled. |
257
- | deviceIsSecure | boolean | Returns true if the device is secure. On iOS, this means that the device has a passcode set. On Android, this means that the device has a PIN, pattern, or password set. |
258
- | reason | string | If weak or better biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string. |
259
- | code | <a href="#biometryerrortype">BiometryErrorType</a> | If weak or better biometry is not available, the error code will be returned here. Otherwise it's an empty string. The error code will be one of the <a href="#biometryerrortype">`BiometryErrorType`</a> enum values, and is consistent across platforms. |
260
- | strongReason | string | If strong biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string.<br><br>On iOS, this will always be the same as `reason`, since all biometry on iOS is strong. |
261
- | strongCode | <a href="#biometryerrortype">BiometryErrorType</a> | If strong biometry is not available, the error code will be returned here. Otherwise it's an empty string. The error code will be one of the <a href="#biometryerrortype">`BiometryErrorType`</a> enum values, and is consistent across platforms.<br><br>On iOS, this will always be the same as `code`, since all biometry on iOS is strong. |
262
-
263
- #### Array
264
-
265
- | Prop | Type | Description |
266
- | :----- | :----- | :----------------------------------------------------------------------------------------------------- |
267
- | length | number | Gets or sets the length of the array. This is a number one higher than the highest index in the array. |
268
-
269
- | Method | Signature | Description |
270
- | :-------------------------------------------------------------------------- | :----------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
271
- | **toString** | () =&gt; string | Returns a string representation of an array. |
272
- | **toLocaleString** | () =&gt; string | Returns a string representation of an array. The elements are converted to string using their toLocaleString methods. |
273
- | **pop** | () =&gt; T \| undefined | Removes the last element from an array and returns it. |
274
- | If the array is empty, undefined is returned and the array is not modified. |
275
- | **push** | (...items: T[]) =&gt; number | Appends new elements to the end of an array, and returns the new length of the array. |
276
- | **concat** | (...items: <a href="#concatarray">ConcatArray</a>&lt;T&gt;[]) =&gt; T[] | Combines two or more arrays. |
277
- | This method returns a new array without modifying any existing arrays. |
278
- | **concat** | (...items: (T \| <a href="#concatarray">ConcatArray</a>&lt;T&gt;)[]) =&gt; T[] | Combines two or more arrays. |
279
- | This method returns a new array without modifying any existing arrays. |
280
- | **join** | (separator?: string) =&gt; string | Adds all the elements of an array into a string, separated by the specified separator string. |
281
- | **reverse** | () =&gt; T[] | Reverses the elements in an array in place. |
282
- | This method mutates the array and returns a reference to the same array. |
283
- | **shift** | () =&gt; T \| undefined | Removes the first element from an array and returns it. |
284
- | If the array is empty, undefined is returned and the array is not modified. |
285
- | **slice** | (start?: number, end?: number) =&gt; T[] | Returns a copy of a section of an array. |
286
-
287
- For both start and end, a negative index can be used to indicate an offset from the end of the array.
288
- For example, -2 refers to the second to last element of the array. |
289
- | **sort** | (compareFn?: ((a: T, b: T) =&gt; number) \| undefined) =&gt; this | Sorts an array in place.
290
- This method mutates the array and returns a reference to the same array. |
291
- | **splice** | (start: number, deleteCount?: number) =&gt; T[] | Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. |
292
- | **splice** | (start: number, deleteCount: number, ...items: T[]) =&gt; T[] | Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. |
293
- | **unshift** | (...items: T[]) =&gt; number | Inserts new elements at the start of an array, and returns the new length of the array. |
294
- | **indexOf** | (searchElement: T, fromIndex?: number) =&gt; number | Returns the index of the first occurrence of a value in an array, or -1 if it is not present. |
295
- | **lastIndexOf** | (searchElement: T, fromIndex?: number) =&gt; number | Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present. |
296
- | **every** | &lt;S extends T&gt;(predicate: (value: T, index: number, array: T[]) =&gt; value is S, thisArg?: any) =&gt; this is S[] | Determines whether all the members of an array satisfy the specified test. |
297
- | **every** | (predicate: (value: T, index: number, array: T[]) =&gt; unknown, thisArg?: any) =&gt; boolean | Determines whether all the members of an array satisfy the specified test. |
298
- | **some** | (predicate: (value: T, index: number, array: T[]) =&gt; unknown, thisArg?: any) =&gt; boolean | Determines whether the specified callback function returns true for any element of an array. |
299
- | **forEach** | (callbackfn: (value: T, index: number, array: T[]) =&gt; void, thisArg?: any) =&gt; void | Performs the specified action for each element in an array. |
300
- | **map** | &lt;U&gt;(callbackfn: (value: T, index: number, array: T[]) =&gt; U, thisArg?: any) =&gt; U[] | Calls a defined callback function on each element of an array, and returns an array that contains the results. |
301
- | **filter** | &lt;S extends T&gt;(predicate: (value: T, index: number, array: T[]) =&gt; value is S, thisArg?: any) =&gt; S[] | Returns the elements of an array that meet the condition specified in a callback function. |
302
- | **filter** | (predicate: (value: T, index: number, array: T[]) =&gt; unknown, thisArg?: any) =&gt; T[] | Returns the elements of an array that meet the condition specified in a callback function. |
303
- | **reduce** | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) =&gt; T) =&gt; T | Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. |
304
- | **reduce** | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) =&gt; T, initialValue: T) =&gt; T | |
305
- | **reduce** | &lt;U&gt;(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) =&gt; U, initialValue: U) =&gt; U | Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. |
306
- | **reduceRight** | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) =&gt; T) =&gt; T | Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. |
307
- | **reduceRight** | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) =&gt; T, initialValue: T) =&gt; T | |
308
- | **reduceRight** | &lt;U&gt;(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) =&gt; U, initialValue: U) =&gt; U | Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. |
309
-
310
- #### ConcatArray
311
-
312
- | Prop | Type |
313
- | :----- | :----- |
314
- | length | number |
315
-
316
- | Method | Signature |
317
- | :-------- | :--------------------------------------- |
318
- | **join** | (separator?: string) =&gt; string |
319
- | **slice** | (start?: number, end?: number) =&gt; T[] |
265
+ | Property | Type | Description |
266
+ | :------------------------ | :------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
267
+ | isAvailable | boolean | True if the device supports _at least_ weak biometric authentication _by apps_ and the current user has enrolled in some form of biometry. Note that if `strongBiometryIsAvailable` is true, this will also be true. |
268
+ | strongBiometryIsAvailable | boolean | True if the device has strong biometric authentication capability and the current user has enrolled in that strong biometry.<br><br>On iOS this value and `isAvailable` will always be the same, since iOS only supports strong biometry.<br><br>On Android, for example, if the device supports both fingerprint and face authentication by apps, and the user has enrolled only in face authentication, and Android considers face authentication on that device to be weak, then `isAvailable` will be true but this value will be false. |
269
+ | biometryType | <a href="#biometrytype">BiometryType</a> | The primary (most secure) type of biometry supported by the device. Note that _supported_ is not the same as _available_, which requires the biometry to be enrolled. |
270
+ | biometryTypes | BiometryType[] | All of the biometry types supported by the hardware on the device, _whether or not it is actually available for use by apps_. If no biometry is supported, i.e. `biometryType === <a href="#biometrytype">BiometryType.none`</a>, this will be an empty array.<br><br>Note that _supported_ is not the same as _available_, which requires the biometry to be enrolled. |
271
+ | deviceIsSecure | boolean | Returns true if the device is secure. On iOS, this means that the device has a passcode set. On Android, this means that the device has a PIN, pattern, or password set. |
272
+ | reason | string | If weak or better biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string. |
273
+ | code | <a href="#biometryerrortype">BiometryErrorType</a> | If weak or better biometry is not available, the error code will be returned here. Otherwise it's an empty string. The error code will be one of the <a href="#biometryerrortype">`BiometryErrorType`</a> enum values, and is consistent across platforms. |
274
+ | strongReason | string | If strong biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string.<br><br>On iOS, this will always be the same as `reason`, since all biometry on iOS is strong. |
275
+ | strongCode | <a href="#biometryerrortype">BiometryErrorType</a> | If strong biometry is not available, the error code will be returned here. Otherwise it's an empty string. The error code will be one of the <a href="#biometryerrortype">`BiometryErrorType`</a> enum values, and is consistent across platforms.<br><br>On iOS, this will always be the same as `code`, since all biometry on iOS is strong. |
320
276
 
321
277
  #### AuthenticateOptions
322
278
 
@@ -1,5 +1,7 @@
1
1
  ext {
2
- androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
2
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
3
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
4
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
3
5
  }
4
6
 
5
7
  buildscript {
@@ -8,7 +10,7 @@ buildscript {
8
10
  mavenCentral()
9
11
  }
10
12
  dependencies {
11
- classpath 'com.android.tools.build:gradle:8.2.2'
13
+ classpath 'com.android.tools.build:gradle:8.13.0'
12
14
  }
13
15
  }
14
16
 
@@ -16,12 +18,12 @@ apply plugin: 'com.android.library'
16
18
 
17
19
  android {
18
20
  namespace "com.aparajita.capacitor.biometricauth"
19
- compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
21
+ compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
20
22
  defaultConfig {
21
- minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
22
- targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
23
- versionCode 1
24
- versionName "1.0"
23
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
24
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
25
+ versionCode 2
26
+ versionName "9.1.0"
25
27
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
26
28
  }
27
29
  buildTypes {
@@ -34,8 +36,8 @@ android {
34
36
  abortOnError false
35
37
  }
36
38
  compileOptions {
37
- sourceCompatibility JavaVersion.VERSION_17
38
- targetCompatibility JavaVersion.VERSION_17
39
+ sourceCompatibility JavaVersion.VERSION_21
40
+ targetCompatibility JavaVersion.VERSION_21
39
41
  }
40
42
  }
41
43
 
@@ -49,6 +51,6 @@ dependencies {
49
51
  implementation 'androidx.biometric:biometric:1.1.0'
50
52
  implementation fileTree(dir: 'libs', include: ['*.jar'])
51
53
  implementation project(':capacitor-android')
52
- implementation 'androidx.appcompat:appcompat:1.7.0'
53
- implementation 'com.google.android.material:material:1.12.0'
54
+ implementation 'androidx.appcompat:appcompat:1.7.1'
55
+ implementation 'com.google.android.material:material:1.13.0'
54
56
  }
@@ -26,7 +26,7 @@ public class AuthActivity extends AppCompatActivity {
26
26
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
27
27
  executor = this.getMainExecutor();
28
28
  } else {
29
- executor = command -> new Handler(this.getMainLooper()).post(command);
29
+ executor = (command) -> new Handler(this.getMainLooper()).post(command);
30
30
  }
31
31
 
32
32
  BiometricPrompt.PromptInfo.Builder builder =
@@ -158,8 +158,10 @@ public class BiometricAuthNative extends Plugin {
158
158
 
159
159
  result.put("biometryTypes", returnTypes);
160
160
 
161
- KeyguardManager keyguardManager = (KeyguardManager) this.getContext()
162
- .getSystemService(Context.KEYGUARD_SERVICE);
161
+ KeyguardManager keyguardManager =
162
+ (KeyguardManager) this.getContext().getSystemService(
163
+ Context.KEYGUARD_SERVICE
164
+ );
163
165
 
164
166
  if (keyguardManager != null) {
165
167
  result.put("deviceIsSecure", keyguardManager.isKeyguardSecure());
@@ -1,6 +1,6 @@
1
- import { WebPlugin } from '@capacitor/core';
2
1
  import type { PluginListenerHandle } from '@capacitor/core';
3
- import type { AuthenticateOptions, BiometricAuthPlugin, CheckBiometryResult, ResumeListener, BiometryType } from './definitions';
2
+ import { WebPlugin } from '@capacitor/core';
3
+ import type { AuthenticateOptions, BiometricAuthPlugin, BiometryType, CheckBiometryResult, ResumeListener } from './definitions';
4
4
  export declare abstract class BiometricAuthBase extends WebPlugin implements BiometricAuthPlugin {
5
5
  abstract setBiometryType(type: BiometryType | string | Array<BiometryType | string> | undefined): Promise<void>;
6
6
  abstract checkBiometry(): Promise<CheckBiometryResult>;
package/dist/esm/base.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { App } from '@capacitor/app';
2
2
  import { CapacitorException, WebPlugin } from '@capacitor/core';
3
- import { BiometryError } from './definitions';
3
+ import { BiometryError, isBiometryErrorType } from './definitions';
4
4
  export class BiometricAuthBase extends WebPlugin {
5
5
  async authenticate(options) {
6
6
  try {
@@ -9,22 +9,25 @@ export class BiometricAuthBase extends WebPlugin {
9
9
  catch (error) {
10
10
  // error will be an instance of CapacitorException on native platforms,
11
11
  // an instance of BiometryError on the web.
12
- if (error instanceof CapacitorException) {
13
- throw new BiometryError(error.message, error.code);
14
- }
15
- else {
16
- throw error;
17
- }
12
+ throw error instanceof CapacitorException &&
13
+ isBiometryErrorType(error.code)
14
+ ? new BiometryError(error.message, error.code)
15
+ : error;
18
16
  }
19
17
  }
20
18
  async addResumeListener(listener) {
21
19
  return App.addListener('appStateChange', ({ isActive }) => {
22
20
  if (isActive) {
23
- this.checkBiometry()
24
- .then((info) => {
25
- listener(info);
26
- })
27
- .catch(console.error);
21
+ ;
22
+ (async () => {
23
+ try {
24
+ const info = await this.checkBiometry();
25
+ listener(info);
26
+ }
27
+ catch (error) {
28
+ console.error(error);
29
+ }
30
+ })();
28
31
  }
29
32
  });
30
33
  }
@@ -158,11 +158,11 @@ export declare enum BiometryErrorType {
158
158
  biometryNotEnrolled = "biometryNotEnrolled",
159
159
  noDeviceCredential = "noDeviceCredential"
160
160
  }
161
+ export declare function isBiometryErrorType(value: unknown): value is BiometryErrorType;
161
162
  /**
162
163
  * `authenticate()` throws instances of this class.
163
164
  */
164
- export declare class BiometryError {
165
- message: string;
165
+ export declare class BiometryError extends Error {
166
166
  code: BiometryErrorType;
167
167
  constructor(message: string, code: BiometryErrorType);
168
168
  }
@@ -61,12 +61,21 @@ export var BiometryErrorType;
61
61
  BiometryErrorType["biometryNotEnrolled"] = "biometryNotEnrolled";
62
62
  BiometryErrorType["noDeviceCredential"] = "noDeviceCredential";
63
63
  })(BiometryErrorType || (BiometryErrorType = {}));
64
+ export function isBiometryErrorType(value) {
65
+ return (typeof value === 'string' &&
66
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
67
+ Object.values(BiometryErrorType).includes(value));
68
+ }
64
69
  /**
65
70
  * `authenticate()` throws instances of this class.
66
71
  */
67
- export class BiometryError {
72
+ export class BiometryError extends Error {
68
73
  constructor(message, code) {
69
- this.message = message;
74
+ super(message);
70
75
  this.code = code;
76
+ this.name = 'BiometryError';
77
+ // Set the prototype explicitly to ensure instanceof works correctly.
78
+ // This is recommended for custom error classes in TypeScript.
79
+ Object.setPrototypeOf(this, BiometryError.prototype);
71
80
  }
72
81
  }
package/dist/esm/index.js CHANGED
@@ -1,8 +1,17 @@
1
1
  import { registerPlugin } from '@capacitor/core';
2
2
  const proxy = registerPlugin('BiometricAuthNative', {
3
- web: async () => import('./web').then((module) => new module.BiometricAuthWeb()),
4
- ios: async () => import('./native').then((module) => new module.BiometricAuthNative(proxy)),
5
- android: async () => import('./native').then((module) => new module.BiometricAuthNative(proxy)),
3
+ web: async () => {
4
+ const module = await import('./web');
5
+ return new module.BiometricAuthWeb();
6
+ },
7
+ ios: async () => {
8
+ const module = await import('./native');
9
+ return new module.BiometricAuthNative(proxy);
10
+ },
11
+ android: async () => {
12
+ const module = await import('./native');
13
+ return new module.BiometricAuthNative(proxy);
14
+ },
6
15
  });
7
16
  export * from './definitions';
8
17
  export * from './web-utils';
@@ -4,8 +4,8 @@ import { BiometryType } from './definitions';
4
4
  export declare class BiometricAuthNative extends BiometricAuthBase {
5
5
  constructor(capProxy: BiometricAuthPlugin);
6
6
  checkBiometry(): Promise<CheckBiometryResult>;
7
- internalAuthenticate(options?: AuthenticateOptions): Promise<void>;
8
- setBiometryType(type: BiometryType | string | Array<BiometryType | string> | undefined): Promise<void>;
9
- setBiometryIsEnrolled(enrolled: boolean): Promise<void>;
10
- setDeviceIsSecure(isSecure: boolean): Promise<void>;
7
+ internalAuthenticate(_options?: AuthenticateOptions): Promise<void>;
8
+ setBiometryType(_type: BiometryType | string | Array<BiometryType | string> | undefined): Promise<void>;
9
+ setBiometryIsEnrolled(_enrolled: boolean): Promise<void>;
10
+ setDeviceIsSecure(_isSecure: boolean): Promise<void>;
11
11
  }
@@ -1,7 +1,6 @@
1
1
  import { BiometricAuthBase } from './base';
2
2
  import { BiometryErrorType, BiometryType } from './definitions';
3
- /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/require-await */
4
- // eslint-disable-next-line import/prefer-default-export
3
+ /* eslint-disable @typescript-eslint/class-methods-use-this, @typescript-eslint/require-await */
5
4
  export class BiometricAuthNative extends BiometricAuthBase {
6
5
  constructor(capProxy) {
7
6
  super();
@@ -13,6 +12,7 @@ export class BiometricAuthNative extends BiometricAuthBase {
13
12
  capProxy is a proxy of an instance of this class, so it is safe
14
13
  to cast it to this class.
15
14
  */
15
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
16
16
  const proxy = capProxy;
17
17
  /* eslint-disable @typescript-eslint/unbound-method */
18
18
  this.checkBiometry = proxy.checkBiometry;
@@ -22,7 +22,7 @@ export class BiometricAuthNative extends BiometricAuthBase {
22
22
  // @native
23
23
  async checkBiometry() {
24
24
  // Never used, but we have to satisfy the compiler.
25
- return Promise.resolve({
25
+ return {
26
26
  isAvailable: false,
27
27
  strongBiometryIsAvailable: false,
28
28
  biometryType: BiometryType.none,
@@ -32,21 +32,24 @@ export class BiometricAuthNative extends BiometricAuthBase {
32
32
  code: BiometryErrorType.none,
33
33
  strongReason: '',
34
34
  strongCode: BiometryErrorType.none,
35
- });
35
+ };
36
36
  }
37
37
  // @native
38
38
  // On native platforms, this will present the native authentication UI.
39
- async internalAuthenticate(options) { }
39
+ async internalAuthenticate(_options) {
40
+ // This method is implemented natively
41
+ }
40
42
  // Web only, used for simulating biometric authentication.
41
- async setBiometryType(type) {
43
+ async setBiometryType(_type) {
42
44
  console.warn('setBiometryType() is web only');
43
45
  }
44
46
  // Web only, used for simulating biometry enrollment.
45
- async setBiometryIsEnrolled(enrolled) {
47
+ async setBiometryIsEnrolled(_enrolled) {
46
48
  console.warn('setBiometryEnrolled() is web only');
47
49
  }
48
50
  // Web only, used for simulating device security.
49
- async setDeviceIsSecure(isSecure) {
51
+ async setDeviceIsSecure(_isSecure) {
50
52
  console.warn('setDeviceIsSecure() is web only');
51
53
  }
52
54
  }
55
+ /* eslint-enable @typescript-eslint/class-methods-use-this, @typescript-eslint/require-await */
@@ -10,7 +10,6 @@ const kBiometryTypeNameMap = {
10
10
  /**
11
11
  * Return a human-readable name for a BiometryType.
12
12
  */
13
- // eslint-disable-next-line import/prefer-default-export
14
13
  export function getBiometryName(type) {
15
14
  return kBiometryTypeNameMap[type] || '';
16
15
  }
package/dist/esm/web.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BiometricAuthBase } from './base';
2
2
  import { BiometryError, BiometryErrorType, BiometryType } from './definitions';
3
3
  import { getBiometryName } from './web-utils';
4
- // eslint-disable-next-line import/prefer-default-export
4
+ /* eslint-disable @typescript-eslint/require-await */
5
5
  export class BiometricAuthWeb extends BiometricAuthBase {
6
6
  constructor() {
7
7
  super(...arguments);
@@ -24,7 +24,7 @@ export class BiometricAuthWeb extends BiometricAuthBase {
24
24
  reason = 'Biometry is not enrolled';
25
25
  code = BiometryErrorType.biometryNotEnrolled;
26
26
  }
27
- return Promise.resolve({
27
+ return {
28
28
  isAvailable: available,
29
29
  strongBiometryIsAvailable: this.biometryIsEnrolled && this.hasStrongBiometry(),
30
30
  biometryType: this.biometryType,
@@ -32,38 +32,36 @@ export class BiometricAuthWeb extends BiometricAuthBase {
32
32
  deviceIsSecure: this.deviceIsSecure,
33
33
  reason,
34
34
  code,
35
- });
35
+ };
36
36
  }
37
37
  hasStrongBiometry() {
38
38
  return this.biometryTypes.some((type) => type === BiometryType.faceId ||
39
39
  type === BiometryType.touchId ||
40
40
  type === BiometryType.fingerprintAuthentication);
41
41
  }
42
- /* eslint-disable no-alert */
43
42
  // On the web, fake authentication with a confirm dialog.
44
43
  async internalAuthenticate(options) {
45
44
  const result = await this.checkBiometry();
46
45
  // First try biometry if available.
47
- if (result.isAvailable) {
48
- if (confirm(
46
+ if (result.isAvailable &&
47
+ // oxlint-disable-next-line no-alert
48
+ confirm(
49
49
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to use the default value if options?.reason is an empty string
50
50
  (options === null || options === void 0 ? void 0 : options.reason) ||
51
51
  `Authenticate with ${result.biometryTypes
52
52
  .map((type) => getBiometryName(type))
53
53
  .join(' or ')}?`)) {
54
- return;
55
- }
54
+ return;
56
55
  }
57
56
  if (options === null || options === void 0 ? void 0 : options.allowDeviceCredential) {
58
57
  // Either biometry is not available, or the user declined to use it
59
58
  // and device security is allowed.
60
59
  if (result.deviceIsSecure) {
60
+ // oxlint-disable-next-line no-alert
61
61
  if (confirm('Authenticate with device security?')) {
62
62
  return;
63
63
  }
64
- else {
65
- throw new BiometryError('User cancelled', BiometryErrorType.userCancel);
66
- }
64
+ throw new BiometryError('User cancelled', BiometryErrorType.userCancel);
67
65
  }
68
66
  else if (result.isAvailable) {
69
67
  throw new BiometryError('Device is not secure', BiometryErrorType.noDeviceCredential);
@@ -71,12 +69,9 @@ export class BiometricAuthWeb extends BiometricAuthBase {
71
69
  }
72
70
  else if (!result.isAvailable) {
73
71
  // Biometry is not available and device security is not allowed.
74
- if (result.biometryType === BiometryType.none) {
75
- throw new BiometryError('Biometry is not available', BiometryErrorType.biometryNotAvailable);
76
- }
77
- else {
78
- throw new BiometryError('Biometry is not enrolled', BiometryErrorType.biometryNotEnrolled);
79
- }
72
+ throw result.biometryType === BiometryType.none
73
+ ? new BiometryError('Biometry is not available', BiometryErrorType.biometryNotAvailable)
74
+ : new BiometryError('Biometry is not enrolled', BiometryErrorType.biometryNotEnrolled);
80
75
  }
81
76
  // The user declined to use biometry and device credentials not allowed.
82
77
  throw new BiometryError('User cancelled', BiometryErrorType.userCancel);
@@ -84,13 +79,13 @@ export class BiometricAuthWeb extends BiometricAuthBase {
84
79
  // Web only, used for simulating biometric authentication.
85
80
  async setBiometryType(type) {
86
81
  if (type === undefined) {
87
- return Promise.resolve();
82
+ return;
88
83
  }
89
84
  const types = Array.isArray(type) ? type : [type];
90
85
  this.biometryTypes = [];
91
86
  this.biometryType = BiometryType.none;
92
87
  if (types.length === 0) {
93
- return Promise.resolve();
88
+ return;
94
89
  }
95
90
  if (isBiometryTypes(types)) {
96
91
  this.biometryType = types[0];
@@ -99,35 +94,35 @@ export class BiometricAuthWeb extends BiometricAuthBase {
99
94
  }
100
95
  }
101
96
  else {
102
- for (let i = 0; i < types.length; i++) {
103
- // eslint-disable-next-line no-prototype-builtins
104
- if (BiometryType.hasOwnProperty(types[i])) {
105
- const biometryType = BiometryType[types[i]];
97
+ for (const [i, theType] of types.entries()) {
98
+ if (isBiometryType(theType)) {
106
99
  if (this.biometryType === BiometryType.none) {
107
100
  this.biometryTypes = [];
108
101
  }
109
102
  else {
110
- this.biometryTypes.push(biometryType);
103
+ this.biometryTypes.push(theType);
111
104
  }
112
105
  if (i === 0) {
113
- this.biometryType = biometryType;
106
+ this.biometryType = theType;
114
107
  }
115
108
  }
116
109
  }
117
110
  }
118
- return Promise.resolve();
119
111
  }
120
112
  // Web only, used for simulating device unlock security.
121
113
  async setBiometryIsEnrolled(enrolled) {
122
114
  this.biometryIsEnrolled = enrolled;
123
- return Promise.resolve();
124
115
  }
125
116
  // Web only, used for simulating device unlock security.
126
117
  async setDeviceIsSecure(isSecure) {
127
118
  this.deviceIsSecure = isSecure;
128
- return Promise.resolve();
129
119
  }
130
120
  }
121
+ function isBiometryType(value) {
122
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
123
+ return Object.values(BiometryType).includes(value);
124
+ }
131
125
  function isBiometryTypes(value) {
132
- return Object.values(BiometryType).includes(value[0]);
126
+ return value.every((type) => isBiometryType(type));
133
127
  }
128
+ /* eslint-enable @typescript-eslint/require-await */