@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 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 | Default |
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. | <code>1</code> |
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
- boolean useFallback = getIntent().getBooleanExtra("useFallback", false);
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
- if (!useFallback) {
61
- String negativeText = getIntent().getStringExtra("negativeButtonText");
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 byte[] FIXED_IV = new byte[12];
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
- boolean useFallback = Boolean.TRUE.equals(call.getBoolean("useFallback", false));
238
- if (useFallback) {
239
- useFallback = this.deviceHasCredentials();
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
- cipher.init(Cipher.ENCRYPT_MODE, getKey(KEY_ALIAS), new GCMParameterSpec(128, FIXED_IV));
367
- byte[] encodedBytes = cipher.doFinal(stringToEncrypt.getBytes(StandardCharsets.UTF_8));
368
- return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
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[] encryptedData = Base64.decode(stringToDecrypt, Base64.DEFAULT);
383
+ byte[] combined = Base64.decode(stringToDecrypt, Base64.DEFAULT);
373
384
 
374
- Cipher cipher;
375
- cipher = Cipher.getInstance(TRANSFORMATION);
376
- cipher.init(Cipher.DECRYPT_MODE, getKey(KEY_ALIAS), new GCMParameterSpec(128, FIXED_IV));
377
- byte[] decryptedData = cipher.doFinal(encryptedData);
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": "Specifies if should fallback to passcode authentication if biometric authentication is not available.",
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": "Specifies if should fallback to passcode authentication if biometric authentication fails.",
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.2.0"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-native-biometric",
3
- "version": "8.2.0",
3
+ "version": "8.3.1",
4
4
  "description": "This plugin gives access to the native biometric apis for android and iOS",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",