@capgo/capacitor-native-biometric 8.2.0 → 8.3.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/README.md +25 -14
- package/android/src/main/java/ee/forgr/biometric/AuthActivity.java +7 -8
- package/android/src/main/java/ee/forgr/biometric/NativeBiometric.java +55 -17
- package/dist/docs.json +2 -2
- package/dist/esm/definitions.d.ts +6 -0
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/NativeBiometricPlugin/NativeBiometricPlugin.swift +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,6 +119,17 @@ This plugin does NOT provide:
|
|
|
119
119
|
- ❌ Server-side authentication or validation
|
|
120
120
|
- ❌ Root/jailbreak detection (use [@capgo/capacitor-is-root](https://github.com/Cap-go/capacitor-is-root))
|
|
121
121
|
|
|
122
|
+
### Recent Security Improvements (v8.2.0+)
|
|
123
|
+
|
|
124
|
+
**Android Encryption Enhancement**: The Android implementation now uses properly randomized Initialization Vectors (IVs) for AES-GCM encryption of stored credentials. Previous versions used a fixed IV, which is a cryptographic vulnerability.
|
|
125
|
+
|
|
126
|
+
**Automatic Migration**: The plugin automatically handles credentials encrypted with the older method:
|
|
127
|
+
- When reading credentials, it first attempts the new secure format, then falls back to the legacy format if needed
|
|
128
|
+
- When saving credentials, they are always encrypted using the new secure format
|
|
129
|
+
- No action required from users - migration happens transparently on first credential save after update
|
|
130
|
+
|
|
131
|
+
**Recommendation**: After updating to v8.2.0+, users should re-save their credentials to ensure they're encrypted with the improved format. This happens automatically when users authenticate and save credentials again.
|
|
132
|
+
|
|
122
133
|
## Installation (Only supports Capacitor 7)
|
|
123
134
|
|
|
124
135
|
- `npm i @capgo/capacitor-native-biometric`
|
|
@@ -436,9 +447,9 @@ Result from isAvailable() method indicating biometric authentication availabilit
|
|
|
436
447
|
|
|
437
448
|
#### IsAvailableOptions
|
|
438
449
|
|
|
439
|
-
| Prop | Type | Description
|
|
440
|
-
| ----------------- | -------------------- |
|
|
441
|
-
| **`useFallback`** | <code>boolean</code> | Specifies if should fallback to passcode authentication if biometric authentication is not available. |
|
|
450
|
+
| Prop | Type | Description |
|
|
451
|
+
| ----------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
452
|
+
| **`useFallback`** | <code>boolean</code> | Only for iOS. Specifies if should fallback to passcode authentication if biometric authentication is not available. On Android, this parameter is ignored due to BiometricPrompt API constraints: DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive. |
|
|
442
453
|
|
|
443
454
|
|
|
444
455
|
#### PluginListenerHandle
|
|
@@ -450,17 +461,17 @@ Result from isAvailable() method indicating biometric authentication availabilit
|
|
|
450
461
|
|
|
451
462
|
#### BiometricOptions
|
|
452
463
|
|
|
453
|
-
| Prop | Type | Description
|
|
454
|
-
| -------------------------- | --------------------------- |
|
|
455
|
-
| **`reason`** | <code>string</code> |
|
|
456
|
-
| **`title`** | <code>string</code> |
|
|
457
|
-
| **`subtitle`** | <code>string</code> |
|
|
458
|
-
| **`description`** | <code>string</code> |
|
|
459
|
-
| **`negativeButtonText`** | <code>string</code> |
|
|
460
|
-
| **`useFallback`** | <code>boolean</code> | Specifies if should fallback to passcode authentication if biometric authentication fails.
|
|
461
|
-
| **`fallbackTitle`** | <code>string</code> | Only for iOS. Set the text for the fallback button in the authentication dialog. If this property is not specified, the default text is set by the system.
|
|
462
|
-
| **`maxAttempts`** | <code>number</code> | Only for Android. Set a maximum number of attempts for biometric authentication. The maximum allowed by android is 5.
|
|
463
|
-
| **`allowedBiometryTypes`** | <code>BiometryType[]</code> | Only for Android. Specify which biometry types are allowed for authentication. If not specified, all available types will be allowed.
|
|
464
|
+
| Prop | Type | Description | Default |
|
|
465
|
+
| -------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
|
466
|
+
| **`reason`** | <code>string</code> | | |
|
|
467
|
+
| **`title`** | <code>string</code> | | |
|
|
468
|
+
| **`subtitle`** | <code>string</code> | | |
|
|
469
|
+
| **`description`** | <code>string</code> | | |
|
|
470
|
+
| **`negativeButtonText`** | <code>string</code> | | |
|
|
471
|
+
| **`useFallback`** | <code>boolean</code> | Only for iOS. Specifies if should fallback to passcode authentication if biometric authentication fails. On Android, this parameter is ignored due to BiometricPrompt API constraints: DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive. | |
|
|
472
|
+
| **`fallbackTitle`** | <code>string</code> | Only for iOS. Set the text for the fallback button in the authentication dialog. If this property is not specified, the default text is set by the system. | |
|
|
473
|
+
| **`maxAttempts`** | <code>number</code> | Only for Android. Set a maximum number of attempts for biometric authentication. The maximum allowed by android is 5. | <code>1</code> |
|
|
474
|
+
| **`allowedBiometryTypes`** | <code>BiometryType[]</code> | Only for Android. Specify which biometry types are allowed for authentication. If not specified, all available types will be allowed. | |
|
|
464
475
|
|
|
465
476
|
|
|
466
477
|
#### Credentials
|
|
@@ -44,23 +44,22 @@ public class AuthActivity extends AppCompatActivity {
|
|
|
44
44
|
.setSubtitle(getIntent().hasExtra("subtitle") ? getIntent().getStringExtra("subtitle") : null)
|
|
45
45
|
.setDescription(getIntent().hasExtra("description") ? getIntent().getStringExtra("description") : null);
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
// Note: useFallback parameter is ignored on Android (iOS-only feature)
|
|
48
|
+
// Android's BiometricPrompt API has a constraint: when DEVICE_CREDENTIAL authenticator is used,
|
|
49
|
+
// setNegativeButtonText() cannot be called (it will throw IllegalArgumentException).
|
|
50
|
+
// Since this plugin always provides a cancel button for consistency, we cannot support
|
|
51
|
+
// device credential fallback. Users should use system settings to enroll biometrics instead.
|
|
48
52
|
int[] allowedTypes = getIntent().getIntArrayExtra("allowedBiometryTypes");
|
|
49
53
|
|
|
50
54
|
int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG;
|
|
51
|
-
if (useFallback) {
|
|
52
|
-
authenticators |= BiometricManager.Authenticators.DEVICE_CREDENTIAL;
|
|
53
|
-
}
|
|
54
55
|
if (allowedTypes != null) {
|
|
55
56
|
// Filter authenticators based on allowed types
|
|
56
57
|
authenticators = getAllowedAuthenticators(allowedTypes);
|
|
57
58
|
}
|
|
58
59
|
builder.setAllowedAuthenticators(authenticators);
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
builder.setNegativeButtonText(negativeText != null ? negativeText : "Cancel");
|
|
63
|
-
}
|
|
61
|
+
String negativeText = getIntent().getStringExtra("negativeButtonText");
|
|
62
|
+
builder.setNegativeButtonText(negativeText != null ? negativeText : "Cancel");
|
|
64
63
|
|
|
65
64
|
BiometricPrompt.PromptInfo promptInfo = builder.build();
|
|
66
65
|
|
|
@@ -40,6 +40,7 @@ import java.security.UnrecoverableEntryException;
|
|
|
40
40
|
import java.security.cert.CertificateException;
|
|
41
41
|
import java.util.ArrayList;
|
|
42
42
|
import java.util.Objects;
|
|
43
|
+
import javax.crypto.BadPaddingException;
|
|
43
44
|
import javax.crypto.Cipher;
|
|
44
45
|
import javax.crypto.CipherInputStream;
|
|
45
46
|
import javax.crypto.CipherOutputStream;
|
|
@@ -73,7 +74,7 @@ public class NativeBiometric extends Plugin {
|
|
|
73
74
|
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
|
|
74
75
|
private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";
|
|
75
76
|
private static final String AES_MODE = "AES/ECB/PKCS7Padding";
|
|
76
|
-
private static final
|
|
77
|
+
private static final int GCM_IV_LENGTH = 12;
|
|
77
78
|
private static final String ENCRYPTED_KEY = "NativeBiometricKey";
|
|
78
79
|
private static final String NATIVE_BIOMETRIC_SHARED_PREFERENCES = "NativeBiometricSharedPreferences";
|
|
79
80
|
|
|
@@ -234,12 +235,10 @@ public class NativeBiometric extends Plugin {
|
|
|
234
235
|
intent.putExtra("allowedBiometryTypes", types);
|
|
235
236
|
}
|
|
236
237
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
intent.putExtra("useFallback", useFallback);
|
|
238
|
+
// Note: useFallback parameter is ignored on Android (iOS-only feature)
|
|
239
|
+
// Android's BiometricPrompt doesn't support fallback to device credentials when a negative button is present.
|
|
240
|
+
// The API constraint: setNegativeButtonText() and DEVICE_CREDENTIAL authenticator are mutually exclusive.
|
|
241
|
+
// Since we need the negative button for user cancellation, fallback cannot be supported on Android.
|
|
243
242
|
|
|
244
243
|
startActivityForResult(call, intent, "verifyResult");
|
|
245
244
|
}
|
|
@@ -363,18 +362,58 @@ public class NativeBiometric extends Plugin {
|
|
|
363
362
|
private String encryptString(String stringToEncrypt, String KEY_ALIAS) throws GeneralSecurityException, IOException {
|
|
364
363
|
Cipher cipher;
|
|
365
364
|
cipher = Cipher.getInstance(TRANSFORMATION);
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
365
|
+
|
|
366
|
+
// Generate a random IV for each encryption operation
|
|
367
|
+
byte[] iv = new byte[GCM_IV_LENGTH];
|
|
368
|
+
SecureRandom secureRandom = new SecureRandom();
|
|
369
|
+
secureRandom.nextBytes(iv);
|
|
370
|
+
|
|
371
|
+
cipher.init(Cipher.ENCRYPT_MODE, getKey(KEY_ALIAS), new GCMParameterSpec(128, iv));
|
|
372
|
+
byte[] encryptedBytes = cipher.doFinal(stringToEncrypt.getBytes(StandardCharsets.UTF_8));
|
|
373
|
+
|
|
374
|
+
// Prepend IV to the encrypted data
|
|
375
|
+
byte[] combined = new byte[iv.length + encryptedBytes.length];
|
|
376
|
+
System.arraycopy(iv, 0, combined, 0, iv.length);
|
|
377
|
+
System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length);
|
|
378
|
+
|
|
379
|
+
return Base64.encodeToString(combined, Base64.DEFAULT);
|
|
369
380
|
}
|
|
370
381
|
|
|
371
382
|
private String decryptString(String stringToDecrypt, String KEY_ALIAS) throws GeneralSecurityException, IOException {
|
|
372
|
-
byte[]
|
|
383
|
+
byte[] combined = Base64.decode(stringToDecrypt, Base64.DEFAULT);
|
|
373
384
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
385
|
+
// Try new format first (IV prepended to ciphertext)
|
|
386
|
+
// New format: 12-byte IV + ciphertext (plaintext + 16-byte GCM auth tag)
|
|
387
|
+
// We check for > GCM_IV_LENGTH to ensure there's at least some ciphertext beyond just the IV
|
|
388
|
+
// The cipher's doFinal() will validate the auth tag and fail if data is malformed
|
|
389
|
+
if (combined.length >= GCM_IV_LENGTH + 1) {
|
|
390
|
+
try {
|
|
391
|
+
// Extract IV from the beginning of the data
|
|
392
|
+
byte[] iv = new byte[GCM_IV_LENGTH];
|
|
393
|
+
byte[] encryptedData = new byte[combined.length - GCM_IV_LENGTH];
|
|
394
|
+
System.arraycopy(combined, 0, iv, 0, GCM_IV_LENGTH);
|
|
395
|
+
System.arraycopy(combined, GCM_IV_LENGTH, encryptedData, 0, encryptedData.length);
|
|
396
|
+
|
|
397
|
+
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
|
398
|
+
cipher.init(Cipher.DECRYPT_MODE, getKey(KEY_ALIAS), new GCMParameterSpec(128, iv));
|
|
399
|
+
byte[] decryptedData = cipher.doFinal(encryptedData);
|
|
400
|
+
return new String(decryptedData, StandardCharsets.UTF_8);
|
|
401
|
+
} catch (BadPaddingException e) {
|
|
402
|
+
// Authentication tag verification failed (AEADBadTagException) or padding error
|
|
403
|
+
// BadPaddingException is the parent class of AEADBadTagException
|
|
404
|
+
// Likely means data was encrypted with legacy format - fall through to legacy decryption
|
|
405
|
+
} catch (GeneralSecurityException e) {
|
|
406
|
+
// Other security exceptions should not be masked - rethrow
|
|
407
|
+
throw e;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Fallback to legacy format (FIXED_IV - all zeros)
|
|
412
|
+
// This branch handles credentials encrypted with the old vulnerable method
|
|
413
|
+
byte[] LEGACY_FIXED_IV = new byte[12]; // All zeros by default
|
|
414
|
+
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
|
415
|
+
cipher.init(Cipher.DECRYPT_MODE, getKey(KEY_ALIAS), new GCMParameterSpec(128, LEGACY_FIXED_IV));
|
|
416
|
+
byte[] decryptedData = cipher.doFinal(combined);
|
|
378
417
|
return new String(decryptedData, StandardCharsets.UTF_8);
|
|
379
418
|
}
|
|
380
419
|
|
|
@@ -410,8 +449,7 @@ public class NativeBiometric extends Plugin {
|
|
|
410
449
|
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
|
|
411
450
|
)
|
|
412
451
|
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
|
413
|
-
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
|
414
|
-
.setRandomizedEncryptionRequired(false);
|
|
452
|
+
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE);
|
|
415
453
|
|
|
416
454
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
417
455
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || Build.VERSION.SDK_INT > 34) {
|
package/dist/docs.json
CHANGED
|
@@ -352,7 +352,7 @@
|
|
|
352
352
|
{
|
|
353
353
|
"name": "useFallback",
|
|
354
354
|
"tags": [],
|
|
355
|
-
"docs": "
|
|
355
|
+
"docs": "Only for iOS.\nSpecifies if should fallback to passcode authentication if biometric authentication is not available.\nOn Android, this parameter is ignored due to BiometricPrompt API constraints:\nDEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive.",
|
|
356
356
|
"complexTypes": [],
|
|
357
357
|
"type": "boolean"
|
|
358
358
|
}
|
|
@@ -419,7 +419,7 @@
|
|
|
419
419
|
{
|
|
420
420
|
"name": "useFallback",
|
|
421
421
|
"tags": [],
|
|
422
|
-
"docs": "
|
|
422
|
+
"docs": "Only for iOS.\nSpecifies if should fallback to passcode authentication if biometric authentication fails.\nOn Android, this parameter is ignored due to BiometricPrompt API constraints:\nDEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive.",
|
|
423
423
|
"complexTypes": [],
|
|
424
424
|
"type": "boolean | undefined"
|
|
425
425
|
},
|
|
@@ -31,7 +31,10 @@ export interface Credentials {
|
|
|
31
31
|
}
|
|
32
32
|
export interface IsAvailableOptions {
|
|
33
33
|
/**
|
|
34
|
+
* Only for iOS.
|
|
34
35
|
* Specifies if should fallback to passcode authentication if biometric authentication is not available.
|
|
36
|
+
* On Android, this parameter is ignored due to BiometricPrompt API constraints:
|
|
37
|
+
* DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive.
|
|
35
38
|
*/
|
|
36
39
|
useFallback: boolean;
|
|
37
40
|
}
|
|
@@ -77,7 +80,10 @@ export interface BiometricOptions {
|
|
|
77
80
|
description?: string;
|
|
78
81
|
negativeButtonText?: string;
|
|
79
82
|
/**
|
|
83
|
+
* Only for iOS.
|
|
80
84
|
* Specifies if should fallback to passcode authentication if biometric authentication fails.
|
|
85
|
+
* On Android, this parameter is ignored due to BiometricPrompt API constraints:
|
|
86
|
+
* DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive.
|
|
81
87
|
*/
|
|
82
88
|
useFallback?: boolean;
|
|
83
89
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAEA,MAAM,CAAN,IAAY,YAiBX;AAjBD,WAAY,YAAY;IACtB,eAAe;IACf,+CAAQ,CAAA;IACR,MAAM;IACN,uDAAY,CAAA;IACZ,MAAM;IACN,qDAAW,CAAA;IACX,UAAU;IACV,6DAAe,CAAA;IACf,UAAU;IACV,6EAAuB,CAAA;IACvB,UAAU;IACV,6EAAuB,CAAA;IACvB,UAAU;IACV,uDAAY,CAAA;IACZ,2DAA2D;IAC3D,yEAAqB,CAAA;AACvB,CAAC,EAjBW,YAAY,KAAZ,YAAY,QAiBvB;AAED,MAAM,CAAN,IAAY,sBAeX;AAfD,WAAY,sBAAsB;IAChC;;OAEG;IACH,mEAAQ,CAAA;IACR;;;OAGG;IACH,uEAAU,CAAA;IACV;;;OAGG;IACH,mEAAQ,CAAA;AACV,CAAC,EAfW,sBAAsB,KAAtB,sBAAsB,QAejC;AAuGD;;;;;;GAMG;AACH,MAAM,CAAN,IAAY,kBAiEX;AAjED,WAAY,kBAAkB;IAC5B;;OAEG;IACH,6EAAiB,CAAA;IACjB;;;OAGG;IACH,+FAA0B,CAAA;IAC1B;;;OAGG;IACH,2EAAgB,CAAA;IAChB;;;OAGG;IACH,iGAA2B,CAAA;IAC3B;;;OAGG;IACH,+FAA0B,CAAA;IAC1B;;;OAGG;IACH,8FAA0B,CAAA;IAC1B;;;OAGG;IACH,wEAAe,CAAA;IACf;;;OAGG;IACH,kFAAoB,CAAA;IACpB;;;OAGG;IACH,kFAAoB,CAAA;IACpB;;;OAGG;IACH,oFAAqB,CAAA;IACrB;;;OAGG;IACH,8EAAkB,CAAA;IAClB;;;OAGG;IACH,0EAAgB,CAAA;IAChB;;;OAGG;IACH,8EAAkB,CAAA;AACpB,CAAC,EAjEW,kBAAkB,KAAlB,kBAAkB,QAiE7B","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\nexport enum BiometryType {\n // Android, iOS\n NONE = 0,\n // iOS\n TOUCH_ID = 1,\n // iOS\n FACE_ID = 2,\n // Android\n FINGERPRINT = 3,\n // Android\n FACE_AUTHENTICATION = 4,\n // Android\n IRIS_AUTHENTICATION = 5,\n // Android\n MULTIPLE = 6,\n // Android - Device credentials (PIN, pattern, or password)\n DEVICE_CREDENTIAL = 7,\n}\n\nexport enum AuthenticationStrength {\n /**\n * No authentication available, even if PIN is available but useFallback = false\n */\n NONE = 0,\n /**\n * Strong authentication: Face ID on iOS, fingerprints on devices that consider fingerprints strong (Android).\n * Note: PIN/pattern/password is NEVER considered STRONG, even when useFallback = true.\n */\n STRONG = 1,\n /**\n * Weak authentication: Face authentication on Android devices that consider face weak,\n * or PIN/pattern/password if useFallback = true (PIN is always WEAK, never STRONG).\n */\n WEAK = 2,\n}\n\nexport interface Credentials {\n username: string;\n password: string;\n}\n\nexport interface IsAvailableOptions {\n /**\n * Specifies if should fallback to passcode authentication if biometric authentication is not available.\n */\n useFallback: boolean;\n}\n\n/**\n * Result from isAvailable() method indicating biometric authentication availability.\n */\nexport interface AvailableResult {\n /**\n * Whether authentication is available (biometric or fallback if useFallback is true)\n */\n isAvailable: boolean;\n /**\n * The strength of available authentication method (STRONG, WEAK, or NONE)\n */\n authenticationStrength: AuthenticationStrength;\n /**\n * The primary biometry type available on the device.\n * On Android devices with multiple biometry types, this returns MULTIPLE.\n * Use this for display purposes only - always use isAvailable for logic decisions.\n */\n biometryType: BiometryType;\n /**\n * Whether the device has a secure lock screen (PIN, pattern, or password).\n * This is independent of biometric enrollment.\n */\n deviceIsSecure: boolean;\n /**\n * Whether strong biometry (Face ID, Touch ID, or fingerprint on devices that consider it strong)\n * is specifically available, separate from weak biometry or device credentials.\n */\n strongBiometryIsAvailable: boolean;\n /**\n * Error code from BiometricAuthError enum. Only present when isAvailable is false.\n * Indicates why biometric authentication is not available.\n * @see BiometricAuthError\n */\n errorCode?: BiometricAuthError;\n}\n\nexport interface BiometricOptions {\n reason?: string;\n title?: string;\n subtitle?: string;\n description?: string;\n negativeButtonText?: string;\n /**\n * Specifies if should fallback to passcode authentication if biometric authentication fails.\n */\n useFallback?: boolean;\n /**\n * Only for iOS.\n * Set the text for the fallback button in the authentication dialog.\n * If this property is not specified, the default text is set by the system.\n */\n fallbackTitle?: string;\n /**\n * Only for Android.\n * Set a maximum number of attempts for biometric authentication. The maximum allowed by android is 5.\n * @default 1\n */\n maxAttempts?: number;\n /**\n * Only for Android.\n * Specify which biometry types are allowed for authentication.\n * If not specified, all available types will be allowed.\n * @example [BiometryType.FINGERPRINT, BiometryType.FACE_AUTHENTICATION]\n */\n allowedBiometryTypes?: BiometryType[];\n}\n\nexport interface GetCredentialOptions {\n server: string;\n}\n\nexport interface SetCredentialOptions {\n username: string;\n password: string;\n server: string;\n}\n\nexport interface DeleteCredentialOptions {\n server: string;\n}\n\nexport interface IsCredentialsSavedOptions {\n server: string;\n}\n\nexport interface IsCredentialsSavedResult {\n isSaved: boolean;\n}\n\n/**\n * Biometric authentication error codes.\n * These error codes are used in both isAvailable() and verifyIdentity() methods.\n *\n * Keep this in sync with BiometricAuthError in README.md\n * Update whenever `convertToPluginErrorCode` functions are modified\n */\nexport enum BiometricAuthError {\n /**\n * Unknown error occurred\n */\n UNKNOWN_ERROR = 0,\n /**\n * Biometrics are unavailable (no hardware or hardware error)\n * Platform: Android, iOS\n */\n BIOMETRICS_UNAVAILABLE = 1,\n /**\n * User has been locked out due to too many failed attempts\n * Platform: Android, iOS\n */\n USER_LOCKOUT = 2,\n /**\n * No biometrics are enrolled on the device\n * Platform: Android, iOS\n */\n BIOMETRICS_NOT_ENROLLED = 3,\n /**\n * User is temporarily locked out (Android: 30 second lockout)\n * Platform: Android\n */\n USER_TEMPORARY_LOCKOUT = 4,\n /**\n * Authentication failed (user did not authenticate successfully)\n * Platform: Android, iOS\n */\n AUTHENTICATION_FAILED = 10,\n /**\n * App canceled the authentication (iOS only)\n * Platform: iOS\n */\n APP_CANCEL = 11,\n /**\n * Invalid context (iOS only)\n * Platform: iOS\n */\n INVALID_CONTEXT = 12,\n /**\n * Authentication was not interactive (iOS only)\n * Platform: iOS\n */\n NOT_INTERACTIVE = 13,\n /**\n * Passcode/PIN is not set on the device\n * Platform: Android, iOS\n */\n PASSCODE_NOT_SET = 14,\n /**\n * System canceled the authentication (e.g., due to screen lock)\n * Platform: Android, iOS\n */\n SYSTEM_CANCEL = 15,\n /**\n * User canceled the authentication\n * Platform: Android, iOS\n */\n USER_CANCEL = 16,\n /**\n * User chose to use fallback authentication method\n * Platform: Android, iOS\n */\n USER_FALLBACK = 17,\n}\n\n/**\n * Callback type for biometry change listener\n */\nexport type BiometryChangeListener = (result: AvailableResult) => void;\n\nexport interface NativeBiometricPlugin {\n /**\n * Checks if biometric authentication hardware is available.\n * @param {IsAvailableOptions} [options]\n * @returns {Promise<AvailableResult>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n isAvailable(options?: IsAvailableOptions): Promise<AvailableResult>;\n\n /**\n * Adds a listener that is called when the app resumes from background.\n * This is useful to detect if biometry availability has changed while\n * the app was in the background (e.g., user enrolled/unenrolled biometrics).\n *\n * @param eventName - Must be 'biometryChange'\n * @param {BiometryChangeListener} listener - Callback function that receives the updated AvailableResult\n * @returns {Promise<PluginListenerHandle>} Handle to remove the listener\n * @since 7.6.0\n *\n * @example\n * ```typescript\n * const handle = await NativeBiometric.addListener('biometryChange', (result) => {\n * console.log('Biometry availability changed:', result.isAvailable);\n * });\n *\n * // To remove the listener:\n * await handle.remove();\n * ```\n */\n addListener(eventName: 'biometryChange', listener: BiometryChangeListener): Promise<PluginListenerHandle>;\n /**\n * Prompts the user to authenticate with biometrics.\n * @param {BiometricOptions} [options]\n * @returns {Promise<any>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n verifyIdentity(options?: BiometricOptions): Promise<void>;\n /**\n * Gets the stored credentials for a given server.\n * @param {GetCredentialOptions} options\n * @returns {Promise<Credentials>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n getCredentials(options: GetCredentialOptions): Promise<Credentials>;\n /**\n * Stores the given credentials for a given server.\n * @param {SetCredentialOptions} options\n * @returns {Promise<any>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n setCredentials(options: SetCredentialOptions): Promise<void>;\n /**\n * Deletes the stored credentials for a given server.\n * @param {DeleteCredentialOptions} options\n * @returns {Promise<any>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n deleteCredentials(options: DeleteCredentialOptions): Promise<void>;\n /**\n * Checks if credentials are already saved for a given server.\n * @param {IsCredentialsSavedOptions} options\n * @returns {Promise<IsCredentialsSavedResult>}\n * @memberof NativeBiometricPlugin\n * @since 7.3.0\n */\n isCredentialsSaved(options: IsCredentialsSavedOptions): Promise<IsCredentialsSavedResult>;\n\n /**\n * Get the native Capacitor plugin version.\n *\n * @returns Promise that resolves with the plugin version\n * @since 1.0.0\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAEA,MAAM,CAAN,IAAY,YAiBX;AAjBD,WAAY,YAAY;IACtB,eAAe;IACf,+CAAQ,CAAA;IACR,MAAM;IACN,uDAAY,CAAA;IACZ,MAAM;IACN,qDAAW,CAAA;IACX,UAAU;IACV,6DAAe,CAAA;IACf,UAAU;IACV,6EAAuB,CAAA;IACvB,UAAU;IACV,6EAAuB,CAAA;IACvB,UAAU;IACV,uDAAY,CAAA;IACZ,2DAA2D;IAC3D,yEAAqB,CAAA;AACvB,CAAC,EAjBW,YAAY,KAAZ,YAAY,QAiBvB;AAED,MAAM,CAAN,IAAY,sBAeX;AAfD,WAAY,sBAAsB;IAChC;;OAEG;IACH,mEAAQ,CAAA;IACR;;;OAGG;IACH,uEAAU,CAAA;IACV;;;OAGG;IACH,mEAAQ,CAAA;AACV,CAAC,EAfW,sBAAsB,KAAtB,sBAAsB,QAejC;AA6GD;;;;;;GAMG;AACH,MAAM,CAAN,IAAY,kBAiEX;AAjED,WAAY,kBAAkB;IAC5B;;OAEG;IACH,6EAAiB,CAAA;IACjB;;;OAGG;IACH,+FAA0B,CAAA;IAC1B;;;OAGG;IACH,2EAAgB,CAAA;IAChB;;;OAGG;IACH,iGAA2B,CAAA;IAC3B;;;OAGG;IACH,+FAA0B,CAAA;IAC1B;;;OAGG;IACH,8FAA0B,CAAA;IAC1B;;;OAGG;IACH,wEAAe,CAAA;IACf;;;OAGG;IACH,kFAAoB,CAAA;IACpB;;;OAGG;IACH,kFAAoB,CAAA;IACpB;;;OAGG;IACH,oFAAqB,CAAA;IACrB;;;OAGG;IACH,8EAAkB,CAAA;IAClB;;;OAGG;IACH,0EAAgB,CAAA;IAChB;;;OAGG;IACH,8EAAkB,CAAA;AACpB,CAAC,EAjEW,kBAAkB,KAAlB,kBAAkB,QAiE7B","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\nexport enum BiometryType {\n // Android, iOS\n NONE = 0,\n // iOS\n TOUCH_ID = 1,\n // iOS\n FACE_ID = 2,\n // Android\n FINGERPRINT = 3,\n // Android\n FACE_AUTHENTICATION = 4,\n // Android\n IRIS_AUTHENTICATION = 5,\n // Android\n MULTIPLE = 6,\n // Android - Device credentials (PIN, pattern, or password)\n DEVICE_CREDENTIAL = 7,\n}\n\nexport enum AuthenticationStrength {\n /**\n * No authentication available, even if PIN is available but useFallback = false\n */\n NONE = 0,\n /**\n * Strong authentication: Face ID on iOS, fingerprints on devices that consider fingerprints strong (Android).\n * Note: PIN/pattern/password is NEVER considered STRONG, even when useFallback = true.\n */\n STRONG = 1,\n /**\n * Weak authentication: Face authentication on Android devices that consider face weak,\n * or PIN/pattern/password if useFallback = true (PIN is always WEAK, never STRONG).\n */\n WEAK = 2,\n}\n\nexport interface Credentials {\n username: string;\n password: string;\n}\n\nexport interface IsAvailableOptions {\n /**\n * Only for iOS.\n * Specifies if should fallback to passcode authentication if biometric authentication is not available.\n * On Android, this parameter is ignored due to BiometricPrompt API constraints:\n * DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive.\n */\n useFallback: boolean;\n}\n\n/**\n * Result from isAvailable() method indicating biometric authentication availability.\n */\nexport interface AvailableResult {\n /**\n * Whether authentication is available (biometric or fallback if useFallback is true)\n */\n isAvailable: boolean;\n /**\n * The strength of available authentication method (STRONG, WEAK, or NONE)\n */\n authenticationStrength: AuthenticationStrength;\n /**\n * The primary biometry type available on the device.\n * On Android devices with multiple biometry types, this returns MULTIPLE.\n * Use this for display purposes only - always use isAvailable for logic decisions.\n */\n biometryType: BiometryType;\n /**\n * Whether the device has a secure lock screen (PIN, pattern, or password).\n * This is independent of biometric enrollment.\n */\n deviceIsSecure: boolean;\n /**\n * Whether strong biometry (Face ID, Touch ID, or fingerprint on devices that consider it strong)\n * is specifically available, separate from weak biometry or device credentials.\n */\n strongBiometryIsAvailable: boolean;\n /**\n * Error code from BiometricAuthError enum. Only present when isAvailable is false.\n * Indicates why biometric authentication is not available.\n * @see BiometricAuthError\n */\n errorCode?: BiometricAuthError;\n}\n\nexport interface BiometricOptions {\n reason?: string;\n title?: string;\n subtitle?: string;\n description?: string;\n negativeButtonText?: string;\n /**\n * Only for iOS.\n * Specifies if should fallback to passcode authentication if biometric authentication fails.\n * On Android, this parameter is ignored due to BiometricPrompt API constraints:\n * DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive.\n */\n useFallback?: boolean;\n /**\n * Only for iOS.\n * Set the text for the fallback button in the authentication dialog.\n * If this property is not specified, the default text is set by the system.\n */\n fallbackTitle?: string;\n /**\n * Only for Android.\n * Set a maximum number of attempts for biometric authentication. The maximum allowed by android is 5.\n * @default 1\n */\n maxAttempts?: number;\n /**\n * Only for Android.\n * Specify which biometry types are allowed for authentication.\n * If not specified, all available types will be allowed.\n * @example [BiometryType.FINGERPRINT, BiometryType.FACE_AUTHENTICATION]\n */\n allowedBiometryTypes?: BiometryType[];\n}\n\nexport interface GetCredentialOptions {\n server: string;\n}\n\nexport interface SetCredentialOptions {\n username: string;\n password: string;\n server: string;\n}\n\nexport interface DeleteCredentialOptions {\n server: string;\n}\n\nexport interface IsCredentialsSavedOptions {\n server: string;\n}\n\nexport interface IsCredentialsSavedResult {\n isSaved: boolean;\n}\n\n/**\n * Biometric authentication error codes.\n * These error codes are used in both isAvailable() and verifyIdentity() methods.\n *\n * Keep this in sync with BiometricAuthError in README.md\n * Update whenever `convertToPluginErrorCode` functions are modified\n */\nexport enum BiometricAuthError {\n /**\n * Unknown error occurred\n */\n UNKNOWN_ERROR = 0,\n /**\n * Biometrics are unavailable (no hardware or hardware error)\n * Platform: Android, iOS\n */\n BIOMETRICS_UNAVAILABLE = 1,\n /**\n * User has been locked out due to too many failed attempts\n * Platform: Android, iOS\n */\n USER_LOCKOUT = 2,\n /**\n * No biometrics are enrolled on the device\n * Platform: Android, iOS\n */\n BIOMETRICS_NOT_ENROLLED = 3,\n /**\n * User is temporarily locked out (Android: 30 second lockout)\n * Platform: Android\n */\n USER_TEMPORARY_LOCKOUT = 4,\n /**\n * Authentication failed (user did not authenticate successfully)\n * Platform: Android, iOS\n */\n AUTHENTICATION_FAILED = 10,\n /**\n * App canceled the authentication (iOS only)\n * Platform: iOS\n */\n APP_CANCEL = 11,\n /**\n * Invalid context (iOS only)\n * Platform: iOS\n */\n INVALID_CONTEXT = 12,\n /**\n * Authentication was not interactive (iOS only)\n * Platform: iOS\n */\n NOT_INTERACTIVE = 13,\n /**\n * Passcode/PIN is not set on the device\n * Platform: Android, iOS\n */\n PASSCODE_NOT_SET = 14,\n /**\n * System canceled the authentication (e.g., due to screen lock)\n * Platform: Android, iOS\n */\n SYSTEM_CANCEL = 15,\n /**\n * User canceled the authentication\n * Platform: Android, iOS\n */\n USER_CANCEL = 16,\n /**\n * User chose to use fallback authentication method\n * Platform: Android, iOS\n */\n USER_FALLBACK = 17,\n}\n\n/**\n * Callback type for biometry change listener\n */\nexport type BiometryChangeListener = (result: AvailableResult) => void;\n\nexport interface NativeBiometricPlugin {\n /**\n * Checks if biometric authentication hardware is available.\n * @param {IsAvailableOptions} [options]\n * @returns {Promise<AvailableResult>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n isAvailable(options?: IsAvailableOptions): Promise<AvailableResult>;\n\n /**\n * Adds a listener that is called when the app resumes from background.\n * This is useful to detect if biometry availability has changed while\n * the app was in the background (e.g., user enrolled/unenrolled biometrics).\n *\n * @param eventName - Must be 'biometryChange'\n * @param {BiometryChangeListener} listener - Callback function that receives the updated AvailableResult\n * @returns {Promise<PluginListenerHandle>} Handle to remove the listener\n * @since 7.6.0\n *\n * @example\n * ```typescript\n * const handle = await NativeBiometric.addListener('biometryChange', (result) => {\n * console.log('Biometry availability changed:', result.isAvailable);\n * });\n *\n * // To remove the listener:\n * await handle.remove();\n * ```\n */\n addListener(eventName: 'biometryChange', listener: BiometryChangeListener): Promise<PluginListenerHandle>;\n /**\n * Prompts the user to authenticate with biometrics.\n * @param {BiometricOptions} [options]\n * @returns {Promise<any>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n verifyIdentity(options?: BiometricOptions): Promise<void>;\n /**\n * Gets the stored credentials for a given server.\n * @param {GetCredentialOptions} options\n * @returns {Promise<Credentials>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n getCredentials(options: GetCredentialOptions): Promise<Credentials>;\n /**\n * Stores the given credentials for a given server.\n * @param {SetCredentialOptions} options\n * @returns {Promise<any>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n setCredentials(options: SetCredentialOptions): Promise<void>;\n /**\n * Deletes the stored credentials for a given server.\n * @param {DeleteCredentialOptions} options\n * @returns {Promise<any>}\n * @memberof NativeBiometricPlugin\n * @since 1.0.0\n */\n deleteCredentials(options: DeleteCredentialOptions): Promise<void>;\n /**\n * Checks if credentials are already saved for a given server.\n * @param {IsCredentialsSavedOptions} options\n * @returns {Promise<IsCredentialsSavedResult>}\n * @memberof NativeBiometricPlugin\n * @since 7.3.0\n */\n isCredentialsSaved(options: IsCredentialsSavedOptions): Promise<IsCredentialsSavedResult>;\n\n /**\n * Get the native Capacitor plugin version.\n *\n * @returns Promise that resolves with the plugin version\n * @since 1.0.0\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
|
|
@@ -11,7 +11,7 @@ import LocalAuthentication
|
|
|
11
11
|
|
|
12
12
|
@objc(NativeBiometricPlugin)
|
|
13
13
|
public class NativeBiometricPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
14
|
-
private let pluginVersion: String = "8.
|
|
14
|
+
private let pluginVersion: String = "8.3.1"
|
|
15
15
|
public let identifier = "NativeBiometricPlugin"
|
|
16
16
|
public let jsName = "NativeBiometric"
|
|
17
17
|
public let pluginMethods: [CAPPluginMethod] = [
|
package/package.json
CHANGED