@aparajita/capacitor-biometric-auth 2.0.8 β†’ 3.0.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 = '12.0'
15
+ s.ios.deployment_target = '13.0'
16
16
  s.dependency 'Capacitor'
17
17
  s.swift_version = '5.1'
18
18
  end
package/README.md CHANGED
@@ -2,19 +2,19 @@
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 3](https://capacitorjs.com) provides access to native biometry on iOS and Android. It supports every type of biometry and every configuration option on both platforms. In addition, biometry is simulated on the web so you can test your logic without making any changes to your code.
5
+ This plugin for [Capacitor 4](https://capacitorjs.com) provides access to native biometry on iOS and Android. It supports every type of biometry and every configuration option on both platforms. In addition, biometry is simulated on the web so you can test your logic without making any changes to your code.
6
6
 
7
- πŸ‘‰ **NOTE:** This plugin only works with Capacitor 3. If you are upgrading from the Capacitor 2 version, note that the plugin name has changed to `BiometricAuth`.
7
+ πŸ‘‰ **NOTE:** This plugin only works with Capacitor 4. If you are upgrading from the Capacitor 2 version, note that the plugin name has changed to `BiometricAuth`.
8
8
 
9
- πŸ›‘ **IMPORTANT:** If you are upgrading from a version prior to 2.0.5, please note that `androidMaxAttempts` in [`AuthenticateOptions`](#authenticateoptions) is now the maximum consecutive failed attempts that are allowed, vs. the previous behavior which was maximum consecutive failed attempts + 1 (which was a bug). So if `androidMaxAttempts` is set to 3, [`authenticate`](#authenticate) will return `BiometryErrorType.authenticationFailed`as soon as the third consecutive attempt fails.
9
+ πŸ›‘ **BREAKING CHANGE:** If you are upgrading from a version prior to 3.0.0, please note that `androidMaxAttempts` is no longer supported. See the documentation for [`authenticate()`](#authenticate) for more information.
10
10
 
11
11
  ## Demos
12
12
 
13
13
  Here is `capacitor-biometric-auth` running on the [demo app](https://github.com/aparajita/capacitor-biometric-auth-demo) on both iOS and Android.
14
14
 
15
- |iOS|Android|
16
- |---|-------|
17
- |<video src="https://user-images.githubusercontent.com/22218/177023147-1f9abce4-2dc3-4157-8bf1-d8d9fdba3977.mp4" width="352" />|<video src="https://user-images.githubusercontent.com/22218/177023168-d7c18a4b-a2f9-49f9-ae39-40884219c128.mp4" width="365" />|
15
+ | iOS | Android |
16
+ | :----------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- |
17
+ | <video src="https://user-images.githubusercontent.com/22218/177023147-1f9abce4-2dc3-4157-8bf1-d8d9fdba3977.mp4" width="352" /> | <video src="https://user-images.githubusercontent.com/22218/177023168-d7c18a4b-a2f9-49f9-ae39-40884219c128.mp4" width="365" /> |
18
18
 
19
19
  ## Installation
20
20
 
@@ -22,7 +22,7 @@ Here is `capacitor-biometric-auth` running on the [demo app](https://github.com/
22
22
  pnpm add @aparajita/capacitor-biometric-auth
23
23
  ```
24
24
 
25
- Not using [pnpm](https://pnpm.js.org/)? You owe it to yourself to give it a try. It’s faster, better with monorepos, and uses *way, way* less disk space than the alternatives.
25
+ Not using [pnpm](https://pnpm.js.org/)? You owe it to yourself to give it a try. It’s faster, better with monorepos, and uses _way, way_ less disk space than the alternatives.
26
26
 
27
27
  ## Usage
28
28
 
@@ -30,7 +30,7 @@ The API is extensively documented in the [TypeScript definitions file](src/defin
30
30
 
31
31
  ### Checking availability
32
32
 
33
- Before giving the user the option to use biometry (such as displaying a biometry icon), call [`checkBiometry`](#checkbiometry) and inspect the [`CheckBiometryResult`](#checkbiometryresult) to see what (if any) biometry is available on the device. Note that `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` will tell you why.
33
+ Before giving the user the option to use biometry (such as displaying a biometry icon), call [`checkBiometry`](#checkbiometry) and inspect the [`CheckBiometryResult`](#checkbiometryresult) to see what (if any) biometry is available on the device. Note that `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` will tell you why.
34
34
 
35
35
  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.
36
36
 
@@ -40,10 +40,10 @@ Once you have determined that biometry is available, to initiate biometric authe
40
40
 
41
41
  If authentication succeeds, the Promise resolves. If authentication fails, the Promise is rejected with a `BiometryError`, which has two properties:
42
42
 
43
- | Property | Type | Description |
44
- | :---------- |:--------------------------------------------------------------------------------------------------------| :------------- |
45
- | message | string | A description of the error suitable for debugging |
46
- | code | [BiometryErrorType](https://github.com/aparajita/capacitor-biometric-auth/blob/main/src/definitions.ts) | What caused the error |
43
+ | Property | Type | Description |
44
+ | :------- | :------------------------------------------------------------------------------------------------------ | :------------------------------------------------ |
45
+ | message | string | A description of the error suitable for debugging |
46
+ | code | [BiometryErrorType](https://github.com/aparajita/capacitor-biometric-auth/blob/main/src/definitions.ts) | What caused the error |
47
47
 
48
48
  ## Biometry support
49
49
 
@@ -60,15 +60,16 @@ On iOS, Touch ID and Face ID are supported.
60
60
  On Android, fingerprint, face, and iris authentication are supported. Note that if a device supports more than one type of biometry, the plugin will only present the primary type, which is determined by the system.
61
61
 
62
62
  ## API
63
+
63
64
  <docgen-index>
64
65
 
65
- * [`checkBiometry()`](#checkbiometry)
66
- * [`setBiometryType(...)`](#setbiometrytype)
67
- * [`authenticate(...)`](#authenticate)
68
- * [`addResumeListener(...)`](#addresumelistener)
69
- * [Interfaces](#interfaces)
70
- * [Type Aliases](#type-aliases)
71
- * [Enums](#enums)
66
+ - [`checkBiometry()`](#checkbiometry)
67
+ - [`setBiometryType(...)`](#setbiometrytype)
68
+ - [`authenticate(...)`](#authenticate)
69
+ - [`addResumeListener(...)`](#addresumelistener)
70
+ - [Interfaces](#interfaces)
71
+ - [Type Aliases](#type-aliases)
72
+ - [Enums](#enums)
72
73
 
73
74
  </docgen-index>
74
75
  <docgen-api>
@@ -84,8 +85,7 @@ Check to see what biometry type (if any) is available.
84
85
 
85
86
  **Returns:** Promise&lt;<a href="#checkbiometryresult">CheckBiometryResult</a>&gt;
86
87
 
87
- --------------------
88
-
88
+ ---
89
89
 
90
90
  ### setBiometryType(...)
91
91
 
@@ -95,12 +95,11 @@ setBiometryType(type: BiometryType | string | undefined) => void
95
95
 
96
96
  web only<br><br>On the web, this method allows you to dynamically simulate different types of biometry. You may either pass a <a href="#biometrytype">`BiometryType`</a> enum or the string name of a <a href="#biometrytype">`BiometryType`</a>. If a string is passed and it isn't a valid value, nothing happens.
97
97
 
98
- | Param | Type |
99
- | :---------- | :--------------------------------------------------------------- |
100
- |type|string \| <a href="#biometrytype">BiometryType</a>|
101
-
102
- --------------------
98
+ | Param | Type |
99
+ | :---- | :------------------------------------------------- |
100
+ | type | string \| <a href="#biometrytype">BiometryType</a> |
103
101
 
102
+ ---
104
103
 
105
104
  ### authenticate(...)
106
105
 
@@ -108,14 +107,13 @@ web only<br><br>On the web, this method allows you to dynamically simulate diffe
108
107
  authenticate(options?: AuthenticateOptions) => Promise<void>
109
108
  ```
110
109
 
111
- Prompt the user for authentication. If authorization fails for any reason, the promise is rejected with a `BiometryError`.
110
+ Prompt the user for authentication. If authorization fails for any reason, the promise is rejected with a `BiometryError`.<br><br>For detailed information about the behavior on iOS, see:<br><br>https://developer.apple.com/documentation/localauthentication/lapolicy/deviceownerauthenticationwithbiometrics<br><br>Android imposes a limit of 5 failed attempts. If `allowDeviceCredential` is `true`, the user will then be presented with a device credential prompt. If `allowDeviceCredential` is `false`, `authenticate()` will reject with a `BiometryErrorType` of `biometryLockout`, after which the user will have to wait 30 seconds before being allowed to authenticate again.
112
111
 
113
- | Param | Type |
114
- | :------------- | :------------------------------------------------------------------- |
115
- |options|<a href="#authenticateoptions">AuthenticateOptions</a>|
116
-
117
- --------------------
112
+ | Param | Type |
113
+ | :------ | :----------------------------------------------------- |
114
+ | options | <a href="#authenticateoptions">AuthenticateOptions</a> |
118
115
 
116
+ ---
119
117
 
120
118
  ### addResumeListener(...)
121
119
 
@@ -125,70 +123,61 @@ addResumeListener(listener: ResumeListener) => Promise<PluginListenerHandle>
125
123
 
126
124
  Register a function that will be called when the app resumes. The function will be passed the result of `checkBiometry()`.
127
125
 
128
- | Param | Type |
129
- | :-------------- | :--------------------------------------------------------- |
130
- |listener|<a href="#resumelistener">ResumeListener</a>|
126
+ | Param | Type |
127
+ | :------- | :------------------------------------------- |
128
+ | listener | <a href="#resumelistener">ResumeListener</a> |
131
129
 
132
130
  **Returns:** Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;
133
131
 
134
- --------------------
135
-
132
+ ---
136
133
 
137
134
  ### Interfaces
138
135
 
139
-
140
136
  #### CheckBiometryResult
141
137
 
142
- | Prop | Type | Description |
143
- | :------------------ | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
144
- |isAvailable|boolean| True if the device has biometric authentication capability and the current user has enrolled in biometry. |
145
- |biometryType|<a href="#biometrytype">BiometryType</a>| The type of biometry available on the device. |
146
- |reason|string| If biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string. |
147
-
138
+ | Prop | Type | Description |
139
+ | :----------- | :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ |
140
+ | isAvailable | boolean | True if the device has biometric authentication capability and the current user has enrolled in biometry. |
141
+ | biometryType | <a href="#biometrytype">BiometryType</a> | The type of biometry available on the device. |
142
+ | reason | string | If biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string. |
148
143
 
149
144
  #### AuthenticateOptions
150
145
 
151
- | Prop | Type | Description |
152
- | :--------------------------- | :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
153
- |reason|string| The reason for requesting authentication. Displays in the authentication dialog presented to the user. If not supplied, a default message is displayed. |
154
- |cancelTitle|string| iOS: The system presents a cancel button during biometric authentication to let the user abort the authentication attempt. The button appears every time the system asks the user to present a finger registered with Touch ID. For Face ID, the button only appears if authentication fails and the user is prompted to try again. Either way, the user can stop trying to authenticate by tapping the button.<br><br>Android: The text for the negative button. This would typically be used as a "Cancel" button, but may be also used to show an alternative method for authentication, such as a screen that asks for a backup password.<br><br>Default: "Cancel" |
155
- |allowDeviceCredential|boolean| If true, allows for authentication using device unlock credentials. Default is false.<br><br>iOS: If biometry is available, enrolled, and not disabled, the system uses that first. After the first Touch ID failure or second Face ID failure, if `iosFallbackTitle` is not an empty string, a fallback button appears in the authentication dialog. If the user taps the fallback button, the system prompts the user for the device passcode or the user’s password. If `iosFallbackTitle` is an empty string, no fallback button will appear.<br><br>If biometry is not available, enrolled and enabled, and a passcode is set, the system immediately prompts the user for the device passcode or user’s password. Authentication fails with the error code `passcodeNotSet` if the device passcode isn’t enabled.<br><br>If a passcode is not set on the device, a `passcodeNotSet` error is returned.<br><br>The system disables passcode authentication after 6 unsuccessful attempts, with progressively increasing delays between attempts.<br><br>The title of the fallback button may be customized by setting `iosFallbackTitle` to a non-empty string.<br><br>Android: The user will first be prompted to authenticate with biometrics, but also given the option to authenticate with their device PIN, pattern, or password by tapping a button in the authentication dialog. The title of the button is supplied by the system. |
156
- |iosFallbackTitle|string| The system presents a fallback button when biometric authentication fails β€” for example, because the system doesn’t recognize the presented finger, or after several failed attempts to recognize the user’s face.<br><br>If `allowDeviceCredential` is false, tapping this button dismisses the authentication dialog and returns the error code userFallback. If undefined, the localized systetm default title is used. Passing an empty string hides the fallback button completely.<br><br>If `allowDeviceCredential` is true and this is undefined, the localized system default title is used. |
157
- |androidTitle|string| Title for the Android dialog. If not supplied, the system default is used. |
158
- |androidSubtitle|string| Subtitle for the Android dialog. If not supplied, the system default is used. |
159
- |androidMaxAttempts|number| When this many consecutive biometric verification attempts fail, `authenticate` returns `BiometryErrorType.authenticationFailed`. The default is 3. |
160
-
146
+ | Prop | Type | Description |
147
+ | :-------------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
148
+ | reason | string | The reason for requesting authentication. Displays in the authentication dialog presented to the user. If not supplied, a default message is displayed. |
149
+ | cancelTitle | string | iOS: The system presents a cancel button during biometric authentication to let the user abort the authentication attempt. The button appears every time the system asks the user to present a finger registered with Touch ID. For Face ID, the button only appears if authentication fails and the user is prompted to try again. Either way, the user can stop trying to authenticate by tapping the button.<br><br>Android: The text for the negative button. This would typically be used as a "Cancel" button, but may be also used to show an alternative method for authentication, such as a screen that asks for a backup password.<br><br>Default: "Cancel" |
150
+ | allowDeviceCredential | boolean | If true, allows for authentication using device unlock credentials. Default is false.<br><br>iOS: If biometry is available, enrolled, and not disabled, the system uses that first. After the first Touch ID failure or second Face ID failure, if `iosFallbackTitle` is not an empty string, a fallback button appears in the authentication dialog. If the user taps the fallback button, the system prompts the user for the device passcode or the user’s password. If `iosFallbackTitle` is an empty string, no fallback button will appear.<br><br>If biometry is not available, enrolled and enabled, and a passcode is set, the system immediately prompts the user for the device passcode or user’s password. Authentication fails with the error code `passcodeNotSet` if the device passcode isn’t enabled.<br><br>If a passcode is not set on the device, a `passcodeNotSet` error is returned.<br><br>The system disables passcode authentication after 6 unsuccessful attempts, with progressively increasing delays between attempts.<br><br>The title of the fallback button may be customized by setting `iosFallbackTitle` to a non-empty string.<br><br>Android: The user will first be prompted to authenticate with biometrics, but also given the option to authenticate with their device PIN, pattern, or password by tapping a button in the authentication dialog. The title of the button is supplied by the system. |
151
+ | iosFallbackTitle | string | The system presents a fallback button when biometric authentication fails β€” for example, because the system doesn’t recognize the presented finger, or after several failed attempts to recognize the user’s face.<br><br>If `allowDeviceCredential` is false, tapping this button dismisses the authentication dialog and returns the error code userFallback. If undefined, the localized systetm default title is used. Passing an empty string hides the fallback button completely.<br><br>If `allowDeviceCredential` is true and this is undefined, the localized system default title is used. |
152
+ | androidTitle | string | Title for the Android dialog. If not supplied, the system default is used. |
153
+ | androidSubtitle | string | Subtitle for the Android dialog. If not supplied, the system default is used. |
161
154
 
162
155
  #### PluginListenerHandle
163
156
 
164
157
  | Method | Signature |
165
- | :---------- | :---------------------------- |
158
+ | :--------- | :--------------------------- |
166
159
  | **remove** | () =&gt; Promise&lt;void&gt; |
167
160
 
168
-
169
161
  ### Type Aliases
170
162
 
171
-
172
163
  #### ResumeListener
173
164
 
174
165
  The signature of the callback passed to `addResumeListener()`.
175
166
 
176
167
  <code>(info: <a href="#checkbiometryresult">CheckBiometryResult</a>): void</code>
177
168
 
178
-
179
169
  ### Enums
180
170
 
181
-
182
171
  #### BiometryType
183
172
 
184
- | Members | Description |
185
- | :------------------------------- | :----------------------------------------------- |
186
- |none| No biometry is available |
187
- |touchId| iOS Touch ID is available |
188
- |faceId| iOS Face ID is available |
189
- |fingerprintAuthentication| Android fingerprint authentication is available |
190
- |faceAuthentication| Android face authentication is available |
191
- |irisAuthentication| Android iris authentication is available |
173
+ | Members | Description |
174
+ | :------------------------ | :---------------------------------------------- |
175
+ | none | No biometry is available |
176
+ | touchId | iOS Touch ID is available |
177
+ | faceId | iOS Face ID is available |
178
+ | fingerprintAuthentication | Android fingerprint authentication is available |
179
+ | faceAuthentication | Android face authentication is available |
180
+ | irisAuthentication | Android iris authentication is available |
192
181
 
193
182
  </docgen-api>
194
183
  </div>
@@ -1,26 +1,26 @@
1
1
  ext {
2
- junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.12'
3
- androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.1'
4
- androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.2.0'
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.3'
4
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.4.0'
5
5
  }
6
6
 
7
7
  buildscript {
8
8
  repositories {
9
9
  google()
10
- jcenter()
10
+ mavenCentral()
11
11
  }
12
12
  dependencies {
13
- classpath 'com.android.tools.build:gradle:4.1.1'
13
+ classpath 'com.android.tools.build:gradle:4.1.3'
14
14
  }
15
15
  }
16
16
 
17
17
  apply plugin: 'com.android.library'
18
18
 
19
19
  android {
20
- compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 30
20
+ compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 32
21
21
  defaultConfig {
22
- minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 21
23
- targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 30
22
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
23
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 32
24
24
  versionCode 1
25
25
  versionName "1.0"
26
26
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -35,27 +35,26 @@ android {
35
35
  abortOnError false
36
36
  }
37
37
  compileOptions {
38
- sourceCompatibility JavaVersion.VERSION_1_8
39
- targetCompatibility JavaVersion.VERSION_1_8
38
+ sourceCompatibility JavaVersion.VERSION_11
39
+ targetCompatibility JavaVersion.VERSION_11
40
40
  }
41
41
  }
42
42
 
43
43
  repositories {
44
44
  google()
45
- jcenter()
46
45
  mavenCentral()
47
46
  }
48
47
 
49
48
 
50
49
  dependencies {
51
- implementation 'androidx.biometric:biometric:1.0.1'
50
+ implementation 'androidx.biometric:biometric:1.1.0'
52
51
  implementation fileTree(dir: 'libs', include: ['*.jar'])
53
52
  implementation project(':capacitor-android')
54
- implementation 'androidx.appcompat:appcompat:1.1.0'
55
- implementation 'com.google.android.material:material:1.1.0'
56
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
57
- implementation 'androidx.navigation:navigation-fragment:2.0.0'
58
- implementation 'androidx.navigation:navigation-ui:2.0.0'
53
+ implementation 'androidx.appcompat:appcompat:1.4.2'
54
+ implementation 'com.google.android.material:material:1.6.1'
55
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
56
+ implementation 'androidx.navigation:navigation-fragment:2.5.1'
57
+ implementation 'androidx.navigation:navigation-ui:2.5.1'
59
58
  testImplementation "junit:junit:$junitVersion"
60
59
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
61
60
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
@@ -3,6 +3,7 @@ package com.aparajita.capacitor.biometricauth;
3
3
  import android.annotation.SuppressLint;
4
4
  import android.app.KeyguardManager;
5
5
  import android.content.Intent;
6
+ import android.hardware.biometrics.BiometricManager;
6
7
  import android.os.Build;
7
8
  import android.os.Bundle;
8
9
  import android.os.Handler;
@@ -13,9 +14,9 @@ import java.util.concurrent.Executor;
13
14
 
14
15
  public class AuthActivity extends AppCompatActivity {
15
16
 
16
- static int attemptCount;
17
17
  static boolean allowDeviceCredential;
18
18
 
19
+ @SuppressLint("WrongConstant")
19
20
  @Override
20
21
  protected void onCreate(Bundle savedInstanceState) {
21
22
  super.onCreate(savedInstanceState);
@@ -57,10 +58,21 @@ public class AuthActivity extends AppCompatActivity {
57
58
  title = "Authenticate";
58
59
  }
59
60
 
60
- builder.setTitle(title);
61
- builder.setSubtitle(subtitle);
62
- builder.setDescription(description);
63
- builder.setDeviceCredentialAllowed(allowDeviceCredential);
61
+ builder.setTitle(title)
62
+ .setSubtitle(subtitle)
63
+ .setDescription(description);
64
+
65
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
66
+ int authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK;
67
+
68
+ if (allowDeviceCredential) {
69
+ authenticators |= BiometricManager.Authenticators.DEVICE_CREDENTIAL;
70
+ }
71
+
72
+ builder.setAllowedAuthenticators(authenticators);
73
+ } else {
74
+ builder.setDeviceCredentialAllowed(allowDeviceCredential);
75
+ }
64
76
 
65
77
  // Android docs say that negative button text should not be set if device credential is allowed
66
78
  if (!allowDeviceCredential) {
@@ -75,8 +87,6 @@ public class AuthActivity extends AppCompatActivity {
75
87
  }
76
88
 
77
89
  BiometricPrompt.PromptInfo promptInfo = builder.build();
78
- attemptCount = 0;
79
-
80
90
  BiometricPrompt prompt = new BiometricPrompt(
81
91
  this,
82
92
  executor,
@@ -101,26 +111,6 @@ public class AuthActivity extends AppCompatActivity {
101
111
  super.onAuthenticationSucceeded(result);
102
112
  finishActivity();
103
113
  }
104
-
105
- @SuppressLint("DefaultLocale")
106
- @Override
107
- public void onAuthenticationFailed() {
108
- super.onAuthenticationFailed();
109
- attemptCount += 1;
110
-
111
- // When allowDeviceCredential is true, I can't seem to force the prompt
112
- // to go away, so skip attempt counting.
113
- if (!allowDeviceCredential && attemptCount >= maxAttempts) {
114
- finishActivity(
115
- BiometryResultType.FAILURE,
116
- 0,
117
- String.format(
118
- "The user reached the maximum of %d attempt(s)",
119
- maxAttempts
120
- )
121
- );
122
- }
123
- }
124
114
  }
125
115
  );
126
116
 
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
4
4
  import android.app.Activity;
5
5
  import android.content.Intent;
6
6
  import android.content.pm.PackageManager;
7
+ import android.os.Build;
7
8
  import androidx.activity.result.ActivityResult;
8
9
  import androidx.biometric.BiometricManager;
9
10
  import androidx.biometric.BiometricPrompt;
@@ -91,17 +92,22 @@ public class BiometricAuth extends Plugin {
91
92
  @PluginMethod
92
93
  public void checkBiometry(PluginCall call) {
93
94
  BiometricManager manager = BiometricManager.from(getContext());
95
+ int biometryResult;
94
96
 
95
- int result = manager.canAuthenticate();
97
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
98
+ biometryResult = manager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK);
99
+ } else {
100
+ biometryResult = manager.canAuthenticate();
101
+ }
96
102
 
97
103
  JSObject ret = new JSObject();
98
- ret.put("isAvailable", result == BiometricManager.BIOMETRIC_SUCCESS);
104
+ ret.put("isAvailable", biometryResult == BiometricManager.BIOMETRIC_SUCCESS);
99
105
  biometryType = getDeviceBiometryType();
100
106
  ret.put("biometryType", biometryType.getType());
101
107
 
102
108
  String reason = "";
103
109
 
104
- switch (result) {
110
+ switch (biometryResult) {
105
111
  case BiometricManager.BIOMETRIC_SUCCESS:
106
112
  break;
107
113
  case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
@@ -113,6 +119,15 @@ public class BiometricAuth extends Plugin {
113
119
  case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
114
120
  reason = "There is no biometric hardware on this device.";
115
121
  break;
122
+ case BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
123
+ reason = "The user can't authenticate because a security vulnerability has been discovered with one or more hardware sensors.";
124
+ break;
125
+ case BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED:
126
+ reason = "The user can't authenticate because the specified options are incompatible with the current Android version.";
127
+ break;
128
+ case BiometricManager.BIOMETRIC_STATUS_UNKNOWN:
129
+ reason = "Unable to determine whether the user can authenticate.";
130
+ break;
116
131
  }
117
132
 
118
133
  ret.put("reason", reason);
@@ -101,12 +101,6 @@ export interface AuthenticateOptions {
101
101
  * Subtitle for the Android dialog. If not supplied, the system default is used.
102
102
  */
103
103
  androidSubtitle?: string;
104
- /**
105
- * When this many consecutive biometric verification attempts fail,
106
- * `authenticate` returns `BiometryErrorType.authenticationFailed`.
107
- * The default is 3.
108
- */
109
- androidMaxAttempts?: number;
110
104
  }
111
105
  /**
112
106
  * If the `authenticate()` method throws an exception, the error object
@@ -176,8 +170,16 @@ export interface BiometricAuthPlugin extends DecoratedNativePlugin {
176
170
  * Prompt the user for authentication. If authorization fails for any reason,
177
171
  * the promise is rejected with a `BiometryError`.
178
172
  *
179
- * @param {AuthenticateOptions} options
180
- * @returns {Promise<void>}
173
+ * For detailed information about the behavior on iOS, see:
174
+ *
175
+ * https://developer.apple.com/documentation/localauthentication/lapolicy/deviceownerauthenticationwithbiometrics
176
+ *
177
+ * Android imposes a limit of 5 failed attempts. If `allowDeviceCredential` is
178
+ * `true`, the user will then be presented with a device credential prompt.
179
+ * If `allowDeviceCredential` is `false`, `authenticate()` will reject with
180
+ * a `BiometryErrorType` of `biometryLockout`, after which the user will have
181
+ * to wait 30 seconds before being allowed to authenticate again.
182
+ *
181
183
  * @rejects {BiometryError}
182
184
  */
183
185
  authenticate: (options?: AuthenticateOptions) => Promise<void>;
@@ -185,7 +187,6 @@ export interface BiometricAuthPlugin extends DecoratedNativePlugin {
185
187
  * Register a function that will be called when the app resumes.
186
188
  * The function will be passed the result of `checkBiometry()`.
187
189
  *
188
- * @param {ResumeListener} listener
189
190
  * @returns {boolean} true if the listener is successfully added
190
191
  */
191
192
  addResumeListener: (listener: ResumeListener) => Promise<PluginListenerHandle>;
package/dist/esm/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { registerPlugin } from '@capacitor/core';
2
2
  import { kPluginName } from './definitions';
3
- import pkg from './pkg';
3
+ import info from './info.json';
4
4
  import { BiometricAuth } from './web';
5
- console.log(`loaded ${pkg.name}`);
5
+ console.log(`loaded ${info.name} v${info.version}`);
6
6
  // Because we are using @aparajita/capacitor-native-decorator,
7
7
  // we have one version of the TS code to rule them all, and there
8
8
  // is no need to lazy load. 😁
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "@aparajita/capacitor-biometric-auth",
3
+ "version": "3.0.0"
4
+ }
package/dist/esm/web.js CHANGED
@@ -1,9 +1,4 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
1
+ import { __decorate } from "tslib";
7
2
  import { native } from '@aparajita/capacitor-native-decorator';
8
3
  import { App } from '@capacitor/app';
9
4
  import { WebPlugin } from '@capacitor/core';
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var core = require('@capacitor/core');
6
+ var tslib = require('tslib');
6
7
  var capacitorNativeDecorator = require('@aparajita/capacitor-native-decorator');
7
8
  var app = require('@capacitor/app');
8
9
 
@@ -64,16 +65,11 @@ class BiometryError {
64
65
  }
65
66
  const kPluginName = 'BiometricAuth';
66
67
 
67
- const pkg = {
68
- name: '@aparajita/capacitor-biometric-auth'
68
+ var info = {
69
+ name: "@aparajita/capacitor-biometric-auth",
70
+ version: "3.0.0"
69
71
  };
70
72
 
71
- var __decorate = (window && window.__decorate) || function (decorators, target, key, desc) {
72
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
73
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
74
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
75
- return c > 3 && r && Object.defineProperty(target, key, r), r;
76
- };
77
73
  const kBiometryTypeNameMap = {
78
74
  [exports.BiometryType.none]: '',
79
75
  [exports.BiometryType.touchId]: 'Touch ID',
@@ -140,10 +136,10 @@ class BiometricAuth extends core.WebPlugin {
140
136
  });
141
137
  }
142
138
  }
143
- __decorate([
139
+ tslib.__decorate([
144
140
  capacitorNativeDecorator.native()
145
141
  ], BiometricAuth.prototype, "checkBiometry", null);
146
- __decorate([
142
+ tslib.__decorate([
147
143
  capacitorNativeDecorator.native()
148
144
  ], BiometricAuth.prototype, "authenticate", null);
149
145
  /**
@@ -156,7 +152,7 @@ function getBiometryName(type) {
156
152
  return kBiometryTypeNameMap[type] || '';
157
153
  }
158
154
 
159
- console.log(`loaded ${pkg.name}`);
155
+ console.log(`loaded ${info.name} v${info.version}`);
160
156
  // Because we are using @aparajita/capacitor-native-decorator,
161
157
  // we have one version of the TS code to rule them all, and there
162
158
  // is no need to lazy load. 😁
package/dist/plugin.js CHANGED
@@ -1,4 +1,4 @@
1
- var capacitorBiometricAuth = (function (exports, core, capacitorNativeDecorator, app) {
1
+ var capacitorDarkMode = (function (exports, core, tslib, capacitorNativeDecorator, app) {
2
2
  'use strict';
3
3
 
4
4
  exports.BiometryType = void 0;
@@ -59,16 +59,11 @@ var capacitorBiometricAuth = (function (exports, core, capacitorNativeDecorator,
59
59
  }
60
60
  const kPluginName = 'BiometricAuth';
61
61
 
62
- const pkg = {
63
- name: '@aparajita/capacitor-biometric-auth'
62
+ var info = {
63
+ name: "@aparajita/capacitor-biometric-auth",
64
+ version: "3.0.0"
64
65
  };
65
66
 
66
- var __decorate = (window && window.__decorate) || function (decorators, target, key, desc) {
67
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
68
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
69
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
70
- return c > 3 && r && Object.defineProperty(target, key, r), r;
71
- };
72
67
  const kBiometryTypeNameMap = {
73
68
  [exports.BiometryType.none]: '',
74
69
  [exports.BiometryType.touchId]: 'Touch ID',
@@ -135,10 +130,10 @@ var capacitorBiometricAuth = (function (exports, core, capacitorNativeDecorator,
135
130
  });
136
131
  }
137
132
  }
138
- __decorate([
133
+ tslib.__decorate([
139
134
  capacitorNativeDecorator.native()
140
135
  ], BiometricAuth.prototype, "checkBiometry", null);
141
- __decorate([
136
+ tslib.__decorate([
142
137
  capacitorNativeDecorator.native()
143
138
  ], BiometricAuth.prototype, "authenticate", null);
144
139
  /**
@@ -151,7 +146,7 @@ var capacitorBiometricAuth = (function (exports, core, capacitorNativeDecorator,
151
146
  return kBiometryTypeNameMap[type] || '';
152
147
  }
153
148
 
154
- console.log(`loaded ${pkg.name}`);
149
+ console.log(`loaded ${info.name} v${info.version}`);
155
150
  // Because we are using @aparajita/capacitor-native-decorator,
156
151
  // we have one version of the TS code to rule them all, and there
157
152
  // is no need to lazy load. 😁
@@ -171,4 +166,4 @@ var capacitorBiometricAuth = (function (exports, core, capacitorNativeDecorator,
171
166
 
172
167
  return exports;
173
168
 
174
- })({}, capacitorExports, capacitorNativeDecorator, app);
169
+ })({}, capacitorExports, tslib, capacitorNativeDecorator, app);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aparajita/capacitor-biometric-auth",
3
- "version": "2.0.8",
3
+ "version": "3.0.0",
4
4
  "description": "Provides access to the native biometric auth APIs for Capacitor apps",
5
5
  "author": "Aparajita Fishman",
6
6
  "license": "MIT",
@@ -19,6 +19,16 @@
19
19
  "*.podspec",
20
20
  "LICENSE"
21
21
  ],
22
+ "commit-and-tag-version": {
23
+ "scripts": {
24
+ "postbump": "pnpm -s builder"
25
+ }
26
+ },
27
+ "ultra": {
28
+ "concurrent": [
29
+ "verify"
30
+ ]
31
+ },
22
32
  "keywords": [
23
33
  "capacitor",
24
34
  "plugin",
@@ -51,68 +61,60 @@
51
61
  "@aparajita/eslint-config-base": "^1.1.5",
52
62
  "@aparajita/prettier-config": "^1.0.0",
53
63
  "@aparajita/swiftly": "^1.0.4",
54
- "@capacitor/core": "^3.6.0",
64
+ "@capacitor/cli": "^4.0.1",
65
+ "@commitlint/cli": "^17.0.3",
66
+ "@commitlint/config-conventional": "^17.0.3",
55
67
  "@ionic/swiftlint-config": "^1.1.2",
56
68
  "@rollup/plugin-json": "^4.1.0",
57
- "@types/node": "^18.6.0",
58
- "@typescript-eslint/eslint-plugin": "^5.30.7",
59
- "@typescript-eslint/parser": "^5.30.7",
60
- "chalk": "^5.0.1",
69
+ "@types/node": "^18.6.3",
70
+ "@typescript-eslint/eslint-plugin": "^5.32.0",
71
+ "@typescript-eslint/parser": "^5.32.0",
61
72
  "commit-and-tag-version": "^10.0.1",
62
- "eslint": "^8.20.0",
73
+ "eslint": "^8.21.0",
63
74
  "eslint-config-prettier": "^8.5.0",
64
75
  "eslint-config-standard": "^17.0.0",
65
- "eslint-import-resolver-typescript": "^3.3.0",
76
+ "eslint-import-resolver-typescript": "^3.4.0",
66
77
  "eslint-plugin-import": "^2.26.0",
67
78
  "eslint-plugin-n": "^15.2.4",
68
79
  "eslint-plugin-prettier": "^4.2.1",
69
80
  "eslint-plugin-promise": "^6.0.0",
81
+ "husky": "^8.0.1",
70
82
  "nodemon": "^2.0.19",
71
83
  "prettier": "^2.7.1",
72
84
  "prettier-plugin-java": "^1.6.2",
73
85
  "rimraf": "^3.0.2",
74
- "rollup": "^2.77.0",
86
+ "rollup": "^2.77.2",
75
87
  "swiftlint": "^1.0.1",
76
- "typescript": "~4.7.4"
88
+ "typescript": "~4.7.4",
89
+ "ultra-runner": "^3.10.5"
77
90
  },
78
91
  "dependencies": {
79
- "@aparajita/capacitor-native-decorator": "^2.0.8",
80
- "@capacitor/android": "^3.6.0",
81
- "@capacitor/app": "^1.1.1",
82
- "@capacitor/ios": "^3.6.0"
83
- },
84
- "peerDependencies": {
85
- "@capacitor/core": "^3.6.0"
92
+ "@aparajita/capacitor-native-decorator": "^3.0.0",
93
+ "@capacitor/android": "^4.0.1",
94
+ "@capacitor/app": "^4.0.1",
95
+ "@capacitor/core": "^4.0.1",
96
+ "@capacitor/ios": "^4.0.1",
97
+ "tslib": "^2.4.0"
86
98
  },
87
99
  "scripts": {
88
- "clean": "rimraf ./dist",
89
- "extract-pkg": "node scripts/packageJsonToTS.js",
90
- "build": "pnpm extract-pkg && pnpm check.fix && pnpm builder",
91
- "build.dev": "pnpm extract-pkg && pnpm check.fix && pnpm builder.dev",
92
- "builder": "pnpm clean && tsc $SOURCE_MAP && pnpm rollup && make-ios-plugin && pnpm docgen",
93
- "builder.dev": "SOURCE_MAP=--sourceMap pnpm builder",
94
- "rollup": "rollup -c rollup.config.mjs",
95
- "watcher": "nodemon -w ./src -w tsconfig.json -w rollup.config.mjs --exec 'pnpm --silent $BUILD' -e ts,json,md",
96
- "watch": "BUILD=build pnpm watcher",
97
- "watch.dev": "BUILD=build.dev pnpm watcher",
98
- "lint": "eslint --cache . && pnpm typecheck",
99
- "lint.fix": "eslint --cache --fix . && pnpm typecheck",
100
- "typecheck": "tsc --noEmit",
101
- "lint.swift": "pnpm swiftlint",
102
- "prettier": "prettier --check \"**/*.{css,html,ts,js,json,java}\"",
103
- "prettier.fix": "pnpm prettier --write",
104
- "swiftlint": "swiftly ios",
105
- "swiftlint.fix": "swiftly --fix ios",
106
- "check": "pnpm lint && pnpm prettier && pnpm swiftlint",
107
- "check.fix": "pnpm lint.fix && pnpm prettier.fix && pnpm swiftlint.fix",
100
+ "extract-info": "node scripts/extractPackageInfo.js",
101
+ "lint.eslint": "eslint --fix --cache --ext .js,.cjs,.mjs,.ts --max-warnings 0",
102
+ "lint.prettier": "prettier --write --cache --list-different",
103
+ "lint.tsc": "tsc --noEmit",
104
+ "lint": "pnpm -s extract-info && pnpm -s lint.eslint . && pnpm -s lint.prettier . && pnpm -s lint.tsc",
105
+ "tsc": "tsc ${SOURCE_MAP}",
106
+ "builder": "pnpm -s extract-info && rimraf dist && pnpm -s tsc && rollup -c rollup.config.mjs && pnpm -s docgen",
107
+ "build": "pnpm -s builder",
108
+ "build.dev": "SOURCE_MAP=--sourceMap pnpm -s builder",
109
+ "watch": "nodemon --exec 'pnpm -s build.dev'",
108
110
  "docgen": "docgen --api BiometricAuthPlugin --output-readme README.md && docgen-format",
109
- "verify": "pnpm verify:ios && pnpm verify:android && pnpm verify:web",
110
- "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin && cd ..",
111
- "verify:android": "cd android && ./gradlew clean build test && cd ..",
112
- "verify:web": "pnpm build",
113
- "push": "git push --follow-tags origin main",
114
- "release.check": "pnpm check.fix && commit-and-tag-version ${VERSION:+-r $VERSION} --dry-run",
115
- "release.prepare": "commit-and-tag-version ${VERSION:+-r $VERSION} && pnpm builder",
116
- "release": "pnpm push && pnpm publish"
111
+ "open.ios": "open ios/Plugin.xcworkspace",
112
+ "open.android": "open -b com.google.android.studio android",
113
+ "verify.ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -quiet && cd ..",
114
+ "verify.android": "cd android && ./gradlew clean build test && cd ..",
115
+ "verify": "pnpm verify.ios && pnpm verify.android",
116
+ "release.pre": "scripts/ensure-clean.sh && pnpm -s lint",
117
+ "release": "pnpm -s release.pre && commit-and-tag-version",
118
+ "release.publish": "pnpm -s release && git push --follow-tags && pnpm publish"
117
119
  }
118
120
  }
package/dist/esm/pkg.d.ts DELETED
@@ -1,4 +0,0 @@
1
- declare const pkg: {
2
- name: string;
3
- };
4
- export default pkg;
package/dist/esm/pkg.js DELETED
@@ -1,4 +0,0 @@
1
- const pkg = {
2
- name: '@aparajita/capacitor-biometric-auth'
3
- };
4
- export default pkg;