@capgo/capacitor-native-biometric 7.5.4 → 8.0.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/CapgoCapacitorNativeBiometric.podspec +1 -1
- package/Package.swift +2 -2
- package/README.md +96 -18
- package/android/build.gradle +9 -9
- package/android/src/main/java/ee/forgr/biometric/AuthActivity.java +3 -1
- package/android/src/main/java/ee/forgr/biometric/NativeBiometric.java +69 -22
- package/dist/docs.json +143 -45
- package/dist/esm/definitions.d.ts +42 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +3 -1
- package/dist/esm/web.js +24 -6
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +23 -6
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +23 -6
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NativeBiometricPlugin/NativeBiometricPlugin.swift +83 -32
- package/package.json +12 -12
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
s.author = 'Martin Donadieu'
|
|
9
9
|
s.source = { :git => 'https://github.com/Cap-go/capacitor-native-biometric', :tag => s.version.to_s }
|
|
10
10
|
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
11
|
-
s.ios.deployment_target = '
|
|
11
|
+
s.ios.deployment_target = '15.0'
|
|
12
12
|
s.dependency 'Capacitor'
|
|
13
13
|
end
|
package/Package.swift
CHANGED
|
@@ -3,14 +3,14 @@ import PackageDescription
|
|
|
3
3
|
|
|
4
4
|
let package = Package(
|
|
5
5
|
name: "CapgoCapacitorNativeBiometric",
|
|
6
|
-
platforms: [.iOS(.
|
|
6
|
+
platforms: [.iOS(.v15)],
|
|
7
7
|
products: [
|
|
8
8
|
.library(
|
|
9
9
|
name: "CapgoCapacitorNativeBiometric",
|
|
10
10
|
targets: ["NativeBiometricPlugin"])
|
|
11
11
|
],
|
|
12
12
|
dependencies: [
|
|
13
|
-
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "
|
|
13
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
|
|
14
14
|
],
|
|
15
15
|
targets: [
|
|
16
16
|
.target(
|
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ A **free**, **comprehensive** biometric authentication plugin with secure creden
|
|
|
18
18
|
- **Flexible fallback** - Optional passcode fallback when biometrics unavailable
|
|
19
19
|
- **Customizable UI** - Full control over prompts, titles, descriptions, button text
|
|
20
20
|
- **Detailed error codes** - Unified error handling across iOS and Android
|
|
21
|
+
- **Resume listener** - Detect biometry availability changes when app returns from background
|
|
21
22
|
- **Modern package management** - Supports both Swift Package Manager (SPM) and CocoaPods (SPM-ready for Capacitor 8)
|
|
22
23
|
|
|
23
24
|
Perfect for banking apps, password managers, authentication flows, and any app requiring secure user verification.
|
|
@@ -40,8 +41,16 @@ async performBiometricVerification(){
|
|
|
40
41
|
|
|
41
42
|
if(!result.isAvailable) return;
|
|
42
43
|
|
|
44
|
+
// Check the biometry type for display purposes
|
|
45
|
+
// IMPORTANT: Always use isAvailable for logic decisions, not biometryType
|
|
43
46
|
const isFaceID = result.biometryType == BiometryType.FACE_ID;
|
|
44
47
|
|
|
48
|
+
// Check if device has PIN/pattern/password set
|
|
49
|
+
console.log('Device is secure:', result.deviceIsSecure);
|
|
50
|
+
|
|
51
|
+
// Check if strong biometry (Face ID, Touch ID, fingerprint) is available
|
|
52
|
+
console.log('Strong biometry available:', result.strongBiometryIsAvailable);
|
|
53
|
+
|
|
45
54
|
const verified = await NativeBiometric.verifyIdentity({
|
|
46
55
|
reason: "For easy log in",
|
|
47
56
|
title: "Log in",
|
|
@@ -69,6 +78,15 @@ NativeBiometric.setCredentials({
|
|
|
69
78
|
NativeBiometric.deleteCredentials({
|
|
70
79
|
server: "www.example.com",
|
|
71
80
|
}).then();
|
|
81
|
+
|
|
82
|
+
// Listen for biometry availability changes when app resumes from background
|
|
83
|
+
const handle = await NativeBiometric.addListener('biometryChange', (result) => {
|
|
84
|
+
console.log('Biometry availability changed:', result.isAvailable);
|
|
85
|
+
console.log('Biometry type:', result.biometryType);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// To remove the listener when no longer needed:
|
|
89
|
+
// await handle.remove();
|
|
72
90
|
```
|
|
73
91
|
|
|
74
92
|
### Biometric Auth Errors
|
|
@@ -94,6 +112,7 @@ This is a plugin specific list of error codes that can be thrown on verifyIdenti
|
|
|
94
112
|
<docgen-index>
|
|
95
113
|
|
|
96
114
|
* [`isAvailable(...)`](#isavailable)
|
|
115
|
+
* [`addListener('biometryChange', ...)`](#addlistenerbiometrychange-)
|
|
97
116
|
* [`verifyIdentity(...)`](#verifyidentity)
|
|
98
117
|
* [`getCredentials(...)`](#getcredentials)
|
|
99
118
|
* [`setCredentials(...)`](#setcredentials)
|
|
@@ -101,6 +120,7 @@ This is a plugin specific list of error codes that can be thrown on verifyIdenti
|
|
|
101
120
|
* [`isCredentialsSaved(...)`](#iscredentialssaved)
|
|
102
121
|
* [`getPluginVersion()`](#getpluginversion)
|
|
103
122
|
* [Interfaces](#interfaces)
|
|
123
|
+
* [Type Aliases](#type-aliases)
|
|
104
124
|
* [Enums](#enums)
|
|
105
125
|
|
|
106
126
|
</docgen-index>
|
|
@@ -127,6 +147,28 @@ Checks if biometric authentication hardware is available.
|
|
|
127
147
|
--------------------
|
|
128
148
|
|
|
129
149
|
|
|
150
|
+
### addListener('biometryChange', ...)
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
addListener(eventName: 'biometryChange', listener: BiometryChangeListener) => Promise<PluginListenerHandle>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Adds a listener that is called when the app resumes from background.
|
|
157
|
+
This is useful to detect if biometry availability has changed while
|
|
158
|
+
the app was in the background (e.g., user enrolled/unenrolled biometrics).
|
|
159
|
+
|
|
160
|
+
| Param | Type | Description |
|
|
161
|
+
| --------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
162
|
+
| **`eventName`** | <code>'biometryChange'</code> | - Must be 'biometryChange' |
|
|
163
|
+
| **`listener`** | <code><a href="#biometrychangelistener">BiometryChangeListener</a></code> | - Callback function that receives the updated <a href="#availableresult">AvailableResult</a> |
|
|
164
|
+
|
|
165
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
166
|
+
|
|
167
|
+
**Since:** 7.6.0
|
|
168
|
+
|
|
169
|
+
--------------------
|
|
170
|
+
|
|
171
|
+
|
|
130
172
|
### verifyIdentity(...)
|
|
131
173
|
|
|
132
174
|
```typescript
|
|
@@ -238,11 +280,14 @@ Get the native Capacitor plugin version.
|
|
|
238
280
|
|
|
239
281
|
Result from isAvailable() method indicating biometric authentication availability.
|
|
240
282
|
|
|
241
|
-
| Prop
|
|
242
|
-
|
|
|
243
|
-
| **`isAvailable`**
|
|
244
|
-
| **`authenticationStrength`**
|
|
245
|
-
| **`
|
|
283
|
+
| Prop | Type | Description |
|
|
284
|
+
| ------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
285
|
+
| **`isAvailable`** | <code>boolean</code> | Whether authentication is available (biometric or fallback if useFallback is true) |
|
|
286
|
+
| **`authenticationStrength`** | <code><a href="#authenticationstrength">AuthenticationStrength</a></code> | The strength of available authentication method (STRONG, WEAK, or NONE) |
|
|
287
|
+
| **`biometryType`** | <code><a href="#biometrytype">BiometryType</a></code> | The primary biometry type available on the device. On Android devices with multiple biometry types, this returns MULTIPLE. Use this for display purposes only - always use isAvailable for logic decisions. |
|
|
288
|
+
| **`deviceIsSecure`** | <code>boolean</code> | Whether the device has a secure lock screen (PIN, pattern, or password). This is independent of biometric enrollment. |
|
|
289
|
+
| **`strongBiometryIsAvailable`** | <code>boolean</code> | Whether strong biometry (Face ID, Touch ID, or fingerprint on devices that consider it strong) is specifically available, separate from weak biometry or device credentials. |
|
|
290
|
+
| **`errorCode`** | <code><a href="#biometricautherror">BiometricAuthError</a></code> | Error code from <a href="#biometricautherror">BiometricAuthError</a> enum. Only present when isAvailable is false. Indicates why biometric authentication is not available. |
|
|
246
291
|
|
|
247
292
|
|
|
248
293
|
#### IsAvailableOptions
|
|
@@ -252,6 +297,13 @@ Result from isAvailable() method indicating biometric authentication availabilit
|
|
|
252
297
|
| **`useFallback`** | <code>boolean</code> | Specifies if should fallback to passcode authentication if biometric authentication is not available. |
|
|
253
298
|
|
|
254
299
|
|
|
300
|
+
#### PluginListenerHandle
|
|
301
|
+
|
|
302
|
+
| Prop | Type |
|
|
303
|
+
| ------------ | ----------------------------------------- |
|
|
304
|
+
| **`remove`** | <code>() => Promise<void></code> |
|
|
305
|
+
|
|
306
|
+
|
|
255
307
|
#### BiometricOptions
|
|
256
308
|
|
|
257
309
|
| Prop | Type | Description | Default |
|
|
@@ -312,6 +364,16 @@ Result from isAvailable() method indicating biometric authentication availabilit
|
|
|
312
364
|
| **`server`** | <code>string</code> |
|
|
313
365
|
|
|
314
366
|
|
|
367
|
+
### Type Aliases
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
#### BiometryChangeListener
|
|
371
|
+
|
|
372
|
+
Callback type for biometry change listener
|
|
373
|
+
|
|
374
|
+
<code>(result: <a href="#availableresult">AvailableResult</a>): void</code>
|
|
375
|
+
|
|
376
|
+
|
|
315
377
|
### Enums
|
|
316
378
|
|
|
317
379
|
|
|
@@ -324,6 +386,19 @@ Result from isAvailable() method indicating biometric authentication availabilit
|
|
|
324
386
|
| **`WEAK`** | <code>2</code> | Weak authentication: Face authentication on Android devices that consider face weak, or PIN/pattern/password if useFallback = true (PIN is always WEAK, never STRONG). |
|
|
325
387
|
|
|
326
388
|
|
|
389
|
+
#### BiometryType
|
|
390
|
+
|
|
391
|
+
| Members | Value |
|
|
392
|
+
| ------------------------- | -------------- |
|
|
393
|
+
| **`NONE`** | <code>0</code> |
|
|
394
|
+
| **`TOUCH_ID`** | <code>1</code> |
|
|
395
|
+
| **`FACE_ID`** | <code>2</code> |
|
|
396
|
+
| **`FINGERPRINT`** | <code>3</code> |
|
|
397
|
+
| **`FACE_AUTHENTICATION`** | <code>4</code> |
|
|
398
|
+
| **`IRIS_AUTHENTICATION`** | <code>5</code> |
|
|
399
|
+
| **`MULTIPLE`** | <code>6</code> |
|
|
400
|
+
|
|
401
|
+
|
|
327
402
|
#### BiometricAuthError
|
|
328
403
|
|
|
329
404
|
| Members | Value | Description |
|
|
@@ -342,19 +417,6 @@ Result from isAvailable() method indicating biometric authentication availabilit
|
|
|
342
417
|
| **`USER_CANCEL`** | <code>16</code> | User canceled the authentication Platform: Android, iOS |
|
|
343
418
|
| **`USER_FALLBACK`** | <code>17</code> | User chose to use fallback authentication method Platform: Android, iOS |
|
|
344
419
|
|
|
345
|
-
|
|
346
|
-
#### BiometryType
|
|
347
|
-
|
|
348
|
-
| Members | Value |
|
|
349
|
-
| ------------------------- | -------------- |
|
|
350
|
-
| **`NONE`** | <code>0</code> |
|
|
351
|
-
| **`TOUCH_ID`** | <code>1</code> |
|
|
352
|
-
| **`FACE_ID`** | <code>2</code> |
|
|
353
|
-
| **`FINGERPRINT`** | <code>3</code> |
|
|
354
|
-
| **`FACE_AUTHENTICATION`** | <code>4</code> |
|
|
355
|
-
| **`IRIS_AUTHENTICATION`** | <code>5</code> |
|
|
356
|
-
| **`MULTIPLE`** | <code>6</code> |
|
|
357
|
-
|
|
358
420
|
</docgen-api>
|
|
359
421
|
## Face ID (iOS)
|
|
360
422
|
|
|
@@ -375,6 +437,22 @@ To use android's BiometricPrompt api you must add the following permission to yo
|
|
|
375
437
|
<uses-permission android:name="android.permission.USE_BIOMETRIC">
|
|
376
438
|
```
|
|
377
439
|
|
|
440
|
+
### Important Note About biometryType on Android
|
|
441
|
+
|
|
442
|
+
The `biometryType` field indicates what biometric hardware is present, but **hardware presence does not guarantee availability**. Some Android devices report face authentication hardware but don't make it available to apps.
|
|
443
|
+
|
|
444
|
+
**Always use `isAvailable` for logic decisions**, not `biometryType`. The `biometryType` field should only be used for display purposes (e.g., showing "Use Face ID" vs "Use Fingerprint" in your UI).
|
|
445
|
+
|
|
446
|
+
## Web Platform
|
|
447
|
+
|
|
448
|
+
This plugin does not support web browsers. On web:
|
|
449
|
+
- `isAvailable()` returns `{ isAvailable: false, ... }` (no error thrown)
|
|
450
|
+
- `addListener()` returns a no-op handle
|
|
451
|
+
- `verifyIdentity()` throws an error
|
|
452
|
+
- Credential methods throw errors
|
|
453
|
+
|
|
454
|
+
This allows you to safely check availability on web without try/catch, but authentication features are only available on iOS and Android.
|
|
455
|
+
|
|
378
456
|
## Contributors
|
|
379
457
|
|
|
380
458
|
[Jonthia](https://github.com/jonthia)
|
package/android/build.gradle
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
ext {
|
|
2
2
|
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
-
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.
|
|
4
|
-
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.
|
|
5
|
-
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.
|
|
3
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
4
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
5
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
buildscript {
|
|
@@ -11,18 +11,18 @@ buildscript {
|
|
|
11
11
|
mavenCentral()
|
|
12
12
|
}
|
|
13
13
|
dependencies {
|
|
14
|
-
classpath 'com.android.tools.build:gradle:8.
|
|
14
|
+
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
apply plugin: 'com.android.library'
|
|
19
19
|
|
|
20
20
|
android {
|
|
21
|
-
namespace "ee.forgr.biometric.capacitornativebiometric"
|
|
22
|
-
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion :
|
|
21
|
+
namespace = "ee.forgr.biometric.capacitornativebiometric"
|
|
22
|
+
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
23
23
|
defaultConfig {
|
|
24
|
-
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion :
|
|
25
|
-
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion :
|
|
24
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
25
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
26
26
|
versionCode 1
|
|
27
27
|
versionName "1.0"
|
|
28
28
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
@@ -34,7 +34,7 @@ android {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
lintOptions {
|
|
37
|
-
abortOnError false
|
|
37
|
+
abortOnError = false
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -23,7 +23,9 @@ public class AuthActivity extends AppCompatActivity {
|
|
|
23
23
|
super.onCreate(savedInstanceState);
|
|
24
24
|
setContentView(R.layout.activity_auth_acitivy);
|
|
25
25
|
|
|
26
|
-
maxAttempts
|
|
26
|
+
// Get maxAttempts with validation: must be between 1 and 5, default to 1
|
|
27
|
+
int rawMaxAttempts = getIntent().getIntExtra("maxAttempts", 1);
|
|
28
|
+
maxAttempts = Math.max(1, Math.min(5, rawMaxAttempts));
|
|
27
29
|
|
|
28
30
|
Executor executor;
|
|
29
31
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
@@ -51,7 +51,7 @@ import org.json.JSONException;
|
|
|
51
51
|
@CapacitorPlugin(name = "NativeBiometric")
|
|
52
52
|
public class NativeBiometric extends Plugin {
|
|
53
53
|
|
|
54
|
-
private final String pluginVersion = "7.
|
|
54
|
+
private final String pluginVersion = "7.5.4";
|
|
55
55
|
|
|
56
56
|
//protected final static int AUTH_CODE = 0102;
|
|
57
57
|
|
|
@@ -77,11 +77,20 @@ public class NativeBiometric extends Plugin {
|
|
|
77
77
|
|
|
78
78
|
private SharedPreferences encryptedSharedPreferences;
|
|
79
79
|
|
|
80
|
-
@
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
@Override
|
|
81
|
+
protected void handleOnResume() {
|
|
82
|
+
super.handleOnResume();
|
|
83
|
+
// Notify listeners when app resumes from background
|
|
84
|
+
JSObject result = checkBiometryAvailability(false);
|
|
85
|
+
notifyListeners("biometryChange", result);
|
|
86
|
+
}
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Check biometry availability and return result as JSObject.
|
|
90
|
+
* This is a helper method used by both isAvailable() and handleOnResume().
|
|
91
|
+
*/
|
|
92
|
+
private JSObject checkBiometryAvailability(boolean useFallback) {
|
|
93
|
+
JSObject ret = new JSObject();
|
|
85
94
|
|
|
86
95
|
BiometricManager biometricManager = BiometricManager.from(getContext());
|
|
87
96
|
|
|
@@ -96,23 +105,30 @@ public class NativeBiometric extends Plugin {
|
|
|
96
105
|
boolean hasWeakBiometric = (weakResult == BiometricManager.BIOMETRIC_SUCCESS);
|
|
97
106
|
|
|
98
107
|
// Check if device has credentials (PIN/pattern/password)
|
|
99
|
-
boolean
|
|
108
|
+
boolean deviceIsSecure = this.deviceHasCredentials();
|
|
109
|
+
boolean fallbackAvailable = useFallback && deviceIsSecure;
|
|
110
|
+
|
|
111
|
+
// Determine biometry type
|
|
112
|
+
int biometryType = detectBiometryType(biometricManager);
|
|
113
|
+
ret.put("biometryType", biometryType);
|
|
114
|
+
|
|
115
|
+
// Device is secure if it has PIN/pattern/password
|
|
116
|
+
ret.put("deviceIsSecure", deviceIsSecure);
|
|
117
|
+
|
|
118
|
+
// Strong biometry is available only if strong biometric check passes
|
|
119
|
+
ret.put("strongBiometryIsAvailable", hasStrongBiometric);
|
|
100
120
|
|
|
101
121
|
// Determine authentication strength
|
|
102
122
|
int authenticationStrength = AUTH_STRENGTH_NONE;
|
|
103
123
|
boolean isAvailable = false;
|
|
104
124
|
|
|
105
125
|
if (hasStrongBiometric) {
|
|
106
|
-
// Strong biometric available (fingerprints on devices that consider them strong)
|
|
107
126
|
authenticationStrength = AUTH_STRENGTH_STRONG;
|
|
108
127
|
isAvailable = true;
|
|
109
128
|
} else if (hasWeakBiometric) {
|
|
110
|
-
// Only weak biometric available (face on devices that consider it weak)
|
|
111
129
|
authenticationStrength = AUTH_STRENGTH_WEAK;
|
|
112
130
|
isAvailable = true;
|
|
113
131
|
} else if (fallbackAvailable) {
|
|
114
|
-
// No biometrics but fallback (PIN/pattern/password) is available
|
|
115
|
-
// PIN/pattern/password is ALWAYS considered WEAK, never STRONG
|
|
116
132
|
authenticationStrength = AUTH_STRENGTH_WEAK;
|
|
117
133
|
isAvailable = true;
|
|
118
134
|
}
|
|
@@ -120,29 +136,58 @@ public class NativeBiometric extends Plugin {
|
|
|
120
136
|
// Handle error codes when authentication is not available
|
|
121
137
|
if (!isAvailable) {
|
|
122
138
|
int biometricManagerErrorCode;
|
|
123
|
-
|
|
124
|
-
// Prefer the error from strong biometric check if it failed
|
|
125
139
|
if (strongResult != BiometricManager.BIOMETRIC_SUCCESS) {
|
|
126
140
|
biometricManagerErrorCode = strongResult;
|
|
127
141
|
} else if (weakResult != BiometricManager.BIOMETRIC_SUCCESS) {
|
|
128
|
-
// Otherwise use error from weak biometric check if it failed
|
|
129
142
|
biometricManagerErrorCode = weakResult;
|
|
130
143
|
} else {
|
|
131
|
-
// No biometrics available at all
|
|
132
|
-
// BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE indicates that biometric hardware is unavailable
|
|
133
|
-
// or cannot be accessed. This constant value may vary across Android versions, so we explicitly
|
|
134
|
-
// use the constant rather than assuming its numeric value.
|
|
135
144
|
biometricManagerErrorCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
|
|
136
145
|
}
|
|
137
|
-
|
|
138
|
-
// Convert BiometricManager error codes to plugin error codes
|
|
139
146
|
int pluginErrorCode = convertBiometricManagerErrorToPluginError(biometricManagerErrorCode);
|
|
140
147
|
ret.put("errorCode", pluginErrorCode);
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
ret.put("isAvailable", isAvailable);
|
|
144
151
|
ret.put("authenticationStrength", authenticationStrength);
|
|
145
|
-
|
|
152
|
+
return ret;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@PluginMethod
|
|
156
|
+
public void isAvailable(PluginCall call) {
|
|
157
|
+
boolean useFallback = Boolean.TRUE.equals(call.getBoolean("useFallback", false));
|
|
158
|
+
JSObject result = checkBiometryAvailability(useFallback);
|
|
159
|
+
call.resolve(result);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Detect the primary biometry type available on the device.
|
|
164
|
+
* Note: Android doesn't provide a direct API to query specific biometry types,
|
|
165
|
+
* so we check for hardware features. This is informational only - always use
|
|
166
|
+
* isAvailable for logic decisions as hardware presence doesn't guarantee availability.
|
|
167
|
+
*/
|
|
168
|
+
private int detectBiometryType(BiometricManager biometricManager) {
|
|
169
|
+
PackageManager pm = getContext().getPackageManager();
|
|
170
|
+
|
|
171
|
+
boolean hasFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
|
|
172
|
+
boolean hasFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
|
|
173
|
+
boolean hasIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
|
|
174
|
+
|
|
175
|
+
int typeCount = 0;
|
|
176
|
+
if (hasFingerprint) typeCount++;
|
|
177
|
+
if (hasFace) typeCount++;
|
|
178
|
+
if (hasIris) typeCount++;
|
|
179
|
+
|
|
180
|
+
if (typeCount > 1) {
|
|
181
|
+
return MULTIPLE; // Multiple biometry types available
|
|
182
|
+
} else if (hasFingerprint) {
|
|
183
|
+
return FINGERPRINT;
|
|
184
|
+
} else if (hasFace) {
|
|
185
|
+
return FACE_AUTHENTICATION;
|
|
186
|
+
} else if (hasIris) {
|
|
187
|
+
return IRIS_AUTHENTICATION;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return NONE;
|
|
146
191
|
}
|
|
147
192
|
|
|
148
193
|
@PluginMethod
|
|
@@ -292,8 +337,10 @@ public class NativeBiometric extends Plugin {
|
|
|
292
337
|
String KEY_ALIAS = call.getString("server", null);
|
|
293
338
|
|
|
294
339
|
if (KEY_ALIAS != null) {
|
|
295
|
-
SharedPreferences sharedPreferences = getContext()
|
|
296
|
-
|
|
340
|
+
SharedPreferences sharedPreferences = getContext().getSharedPreferences(
|
|
341
|
+
NATIVE_BIOMETRIC_SHARED_PREFERENCES,
|
|
342
|
+
Context.MODE_PRIVATE
|
|
343
|
+
);
|
|
297
344
|
String username = sharedPreferences.getString(KEY_ALIAS + "-username", null);
|
|
298
345
|
String password = sharedPreferences.getString(KEY_ALIAS + "-password", null);
|
|
299
346
|
|
package/dist/docs.json
CHANGED
|
@@ -40,6 +40,51 @@
|
|
|
40
40
|
],
|
|
41
41
|
"slug": "isavailable"
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
"name": "addListener",
|
|
45
|
+
"signature": "(eventName: 'biometryChange', listener: BiometryChangeListener) => Promise<PluginListenerHandle>",
|
|
46
|
+
"parameters": [
|
|
47
|
+
{
|
|
48
|
+
"name": "eventName",
|
|
49
|
+
"docs": "- Must be 'biometryChange'",
|
|
50
|
+
"type": "'biometryChange'"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "listener",
|
|
54
|
+
"docs": "- Callback function that receives the updated AvailableResult",
|
|
55
|
+
"type": "BiometryChangeListener"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"returns": "Promise<PluginListenerHandle>",
|
|
59
|
+
"tags": [
|
|
60
|
+
{
|
|
61
|
+
"name": "param",
|
|
62
|
+
"text": "eventName - Must be 'biometryChange'"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "param",
|
|
66
|
+
"text": "listener - Callback function that receives the updated AvailableResult"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "returns",
|
|
70
|
+
"text": "Handle to remove the listener"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "since",
|
|
74
|
+
"text": "7.6.0"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "example",
|
|
78
|
+
"text": "```typescript\nconst handle = await NativeBiometric.addListener('biometryChange', (result) => {\n console.log('Biometry availability changed:', result.isAvailable);\n});\n\n// To remove the listener:\nawait handle.remove();\n```"
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"docs": "Adds a listener that is called when the app resumes from background.\nThis is useful to detect if biometry availability has changed while\nthe app was in the background (e.g., user enrolled/unenrolled biometrics).",
|
|
82
|
+
"complexTypes": [
|
|
83
|
+
"PluginListenerHandle",
|
|
84
|
+
"BiometryChangeListener"
|
|
85
|
+
],
|
|
86
|
+
"slug": "addlistenerbiometrychange-"
|
|
87
|
+
},
|
|
43
88
|
{
|
|
44
89
|
"name": "verifyIdentity",
|
|
45
90
|
"signature": "(options?: BiometricOptions | undefined) => Promise<void>",
|
|
@@ -258,6 +303,29 @@
|
|
|
258
303
|
],
|
|
259
304
|
"type": "AuthenticationStrength"
|
|
260
305
|
},
|
|
306
|
+
{
|
|
307
|
+
"name": "biometryType",
|
|
308
|
+
"tags": [],
|
|
309
|
+
"docs": "The primary biometry type available on the device.\nOn Android devices with multiple biometry types, this returns MULTIPLE.\nUse this for display purposes only - always use isAvailable for logic decisions.",
|
|
310
|
+
"complexTypes": [
|
|
311
|
+
"BiometryType"
|
|
312
|
+
],
|
|
313
|
+
"type": "BiometryType"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"name": "deviceIsSecure",
|
|
317
|
+
"tags": [],
|
|
318
|
+
"docs": "Whether the device has a secure lock screen (PIN, pattern, or password).\nThis is independent of biometric enrollment.",
|
|
319
|
+
"complexTypes": [],
|
|
320
|
+
"type": "boolean"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"name": "strongBiometryIsAvailable",
|
|
324
|
+
"tags": [],
|
|
325
|
+
"docs": "Whether strong biometry (Face ID, Touch ID, or fingerprint on devices that consider it strong)\nis specifically available, separate from weak biometry or device credentials.",
|
|
326
|
+
"complexTypes": [],
|
|
327
|
+
"type": "boolean"
|
|
328
|
+
},
|
|
261
329
|
{
|
|
262
330
|
"name": "errorCode",
|
|
263
331
|
"tags": [
|
|
@@ -290,6 +358,22 @@
|
|
|
290
358
|
}
|
|
291
359
|
]
|
|
292
360
|
},
|
|
361
|
+
{
|
|
362
|
+
"name": "PluginListenerHandle",
|
|
363
|
+
"slug": "pluginlistenerhandle",
|
|
364
|
+
"docs": "",
|
|
365
|
+
"tags": [],
|
|
366
|
+
"methods": [],
|
|
367
|
+
"properties": [
|
|
368
|
+
{
|
|
369
|
+
"name": "remove",
|
|
370
|
+
"tags": [],
|
|
371
|
+
"docs": "",
|
|
372
|
+
"complexTypes": [],
|
|
373
|
+
"type": "() => Promise<void>"
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
},
|
|
293
377
|
{
|
|
294
378
|
"name": "BiometricOptions",
|
|
295
379
|
"slug": "biometricoptions",
|
|
@@ -517,6 +601,54 @@
|
|
|
517
601
|
}
|
|
518
602
|
]
|
|
519
603
|
},
|
|
604
|
+
{
|
|
605
|
+
"name": "BiometryType",
|
|
606
|
+
"slug": "biometrytype",
|
|
607
|
+
"members": [
|
|
608
|
+
{
|
|
609
|
+
"name": "NONE",
|
|
610
|
+
"value": "0",
|
|
611
|
+
"tags": [],
|
|
612
|
+
"docs": ""
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
"name": "TOUCH_ID",
|
|
616
|
+
"value": "1",
|
|
617
|
+
"tags": [],
|
|
618
|
+
"docs": ""
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
"name": "FACE_ID",
|
|
622
|
+
"value": "2",
|
|
623
|
+
"tags": [],
|
|
624
|
+
"docs": ""
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
"name": "FINGERPRINT",
|
|
628
|
+
"value": "3",
|
|
629
|
+
"tags": [],
|
|
630
|
+
"docs": ""
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"name": "FACE_AUTHENTICATION",
|
|
634
|
+
"value": "4",
|
|
635
|
+
"tags": [],
|
|
636
|
+
"docs": ""
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
"name": "IRIS_AUTHENTICATION",
|
|
640
|
+
"value": "5",
|
|
641
|
+
"tags": [],
|
|
642
|
+
"docs": ""
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
"name": "MULTIPLE",
|
|
646
|
+
"value": "6",
|
|
647
|
+
"tags": [],
|
|
648
|
+
"docs": ""
|
|
649
|
+
}
|
|
650
|
+
]
|
|
651
|
+
},
|
|
520
652
|
{
|
|
521
653
|
"name": "BiometricAuthError",
|
|
522
654
|
"slug": "biometricautherror",
|
|
@@ -600,56 +732,22 @@
|
|
|
600
732
|
"docs": "User chose to use fallback authentication method\nPlatform: Android, iOS"
|
|
601
733
|
}
|
|
602
734
|
]
|
|
603
|
-
}
|
|
735
|
+
}
|
|
736
|
+
],
|
|
737
|
+
"typeAliases": [
|
|
604
738
|
{
|
|
605
|
-
"name": "
|
|
606
|
-
"slug": "
|
|
607
|
-
"
|
|
608
|
-
|
|
609
|
-
"name": "NONE",
|
|
610
|
-
"value": "0",
|
|
611
|
-
"tags": [],
|
|
612
|
-
"docs": ""
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
"name": "TOUCH_ID",
|
|
616
|
-
"value": "1",
|
|
617
|
-
"tags": [],
|
|
618
|
-
"docs": ""
|
|
619
|
-
},
|
|
739
|
+
"name": "BiometryChangeListener",
|
|
740
|
+
"slug": "biometrychangelistener",
|
|
741
|
+
"docs": "Callback type for biometry change listener",
|
|
742
|
+
"types": [
|
|
620
743
|
{
|
|
621
|
-
"
|
|
622
|
-
"
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
"name": "FINGERPRINT",
|
|
628
|
-
"value": "3",
|
|
629
|
-
"tags": [],
|
|
630
|
-
"docs": ""
|
|
631
|
-
},
|
|
632
|
-
{
|
|
633
|
-
"name": "FACE_AUTHENTICATION",
|
|
634
|
-
"value": "4",
|
|
635
|
-
"tags": [],
|
|
636
|
-
"docs": ""
|
|
637
|
-
},
|
|
638
|
-
{
|
|
639
|
-
"name": "IRIS_AUTHENTICATION",
|
|
640
|
-
"value": "5",
|
|
641
|
-
"tags": [],
|
|
642
|
-
"docs": ""
|
|
643
|
-
},
|
|
644
|
-
{
|
|
645
|
-
"name": "MULTIPLE",
|
|
646
|
-
"value": "6",
|
|
647
|
-
"tags": [],
|
|
648
|
-
"docs": ""
|
|
744
|
+
"text": "(result: AvailableResult): void",
|
|
745
|
+
"complexTypes": [
|
|
746
|
+
"AvailableResult"
|
|
747
|
+
]
|
|
649
748
|
}
|
|
650
749
|
]
|
|
651
750
|
}
|
|
652
751
|
],
|
|
653
|
-
"typeAliases": [],
|
|
654
752
|
"pluginConfigs": []
|
|
655
753
|
}
|