@capgo/capacitor-native-biometric
Advanced tools
@@ -11,2 +11,3 @@ package ee.forgr.biometric; | ||
| import android.security.keystore.KeyProperties; | ||
| import android.security.keystore.UserNotAuthenticatedException; | ||
| import android.util.Base64; | ||
@@ -43,2 +44,3 @@ import androidx.annotation.NonNull; | ||
| private static final String SHARED_PREFS_NAME = "NativeBiometricSharedPreferences"; | ||
| private static final String SECURE_VALIDITY_SUFFIX = "_validity"; | ||
@@ -50,2 +52,3 @@ private BiometricPrompt biometricPrompt; | ||
| private int counter = 0; | ||
| private int authValidityDuration; | ||
@@ -63,2 +66,18 @@ @Override | ||
| String server = getIntent().getStringExtra("server"); | ||
| if ("setSecureCredentials".equals(mode)) { | ||
| // Not yet persisted — this call establishes the mode for the alias. | ||
| authValidityDuration = Math.max(0, getIntent().getIntExtra("authValidityDuration", 0)); | ||
| } else if ("getSecureCredentials".equals(mode)) { | ||
| authValidityDuration = getStoredAuthValidityDuration(server); | ||
| } | ||
| if (("setSecureCredentials".equals(mode) || "getSecureCredentials".equals(mode)) && authValidityDuration > 0) { | ||
| // Opt-in validity-window mode: try the Keystore operation without a prompt first. | ||
| // If the window already covers us, we can finish immediately with no BiometricPrompt. | ||
| if (tryWithoutPrompt()) { | ||
| return; | ||
| } | ||
| } | ||
| Executor executor; | ||
@@ -120,3 +139,10 @@ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { | ||
| super.onAuthenticationSucceeded(result); | ||
| if ("setSecureCredentials".equals(mode)) { | ||
| boolean isValidityWindowMode = | ||
| ("setSecureCredentials".equals(mode) || "getSecureCredentials".equals(mode)) && authValidityDuration > 0; | ||
| if (isValidityWindowMode) { | ||
| // The prompt carries no CryptoObject in this mode (see tryWithoutPrompt) — the | ||
| // successful authentication merely unlocks the Keystore key for the validity | ||
| // window. Retry the plain cipher operation now that the device is authenticated. | ||
| retryAfterPrompt(); | ||
| } else if ("setSecureCredentials".equals(mode)) { | ||
| handleSetSecureCredentials(result); | ||
@@ -147,2 +173,9 @@ } else if ("getSecureCredentials".equals(mode)) { | ||
| if (("setSecureCredentials".equals(mode) || "getSecureCredentials".equals(mode)) && authValidityDuration > 0) { | ||
| // Validity-window mode: a single authentication unlocks the Keystore key for | ||
| // `authValidityDuration` seconds, so the prompt is not bound to a CryptoObject. | ||
| biometricPrompt.authenticate(promptInfo); | ||
| return; | ||
| } | ||
| BiometricPrompt.CryptoObject cryptoObject; | ||
@@ -292,2 +325,7 @@ if ("setSecureCredentials".equals(mode)) { | ||
| private SecretKey getOrCreateCredentialKey(String server, int accessControl) throws GeneralSecurityException, IOException { | ||
| return getOrCreateCredentialKey(server, accessControl, 0); | ||
| } | ||
| private SecretKey getOrCreateCredentialKey(String server, int accessControl, int authValidityDuration) | ||
| throws GeneralSecurityException, IOException { | ||
| String alias = SECURE_KEY_PREFIX + server; | ||
@@ -298,8 +336,17 @@ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); | ||
| if (ks.containsAlias(alias)) { | ||
| return (SecretKey) ks.getKey(alias, null); | ||
| // If the caller asked for a different auth model than the one the existing key was | ||
| // generated with (per-operation vs. time-based validity window), the key must be | ||
| // regenerated so the new setUserAuthenticationParameters() take effect — Keystore | ||
| // does not allow changing these parameters on an existing key. | ||
| if (getStoredAuthValidityDuration(server) != authValidityDuration) { | ||
| ks.deleteEntry(alias); | ||
| } else { | ||
| return (SecretKey) ks.getKey(alias, null); | ||
| } | ||
| } | ||
| boolean invalidatedByEnrollment = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && accessControl == 1; | ||
| SecretKey key; | ||
| try { | ||
| return buildCredentialKey(alias, invalidatedByEnrollment); | ||
| key = buildCredentialKey(alias, invalidatedByEnrollment, authValidityDuration); | ||
| } catch (ProviderException e) { | ||
@@ -310,14 +357,18 @@ // Xiaomi/MIUI and Oppo/ColorOS Keymasters may reject setInvalidatedByBiometricEnrollment(true) | ||
| try { | ||
| return buildCredentialKey(alias, false); | ||
| key = buildCredentialKey(alias, false, authValidityDuration); | ||
| } catch (ProviderException retryError) { | ||
| throw new GeneralSecurityException("Keystore key generation failed", retryError); | ||
| } | ||
| } else { | ||
| // ProviderException is unchecked; convert it so callers handle it gracefully | ||
| // (return null -> error result) instead of crashing AuthActivity.onCreate. | ||
| throw new GeneralSecurityException("Keystore key generation failed", e); | ||
| } | ||
| // ProviderException is unchecked; convert it so callers handle it gracefully | ||
| // (return null -> error result) instead of crashing AuthActivity.onCreate. | ||
| throw new GeneralSecurityException("Keystore key generation failed", e); | ||
| } | ||
| storeAuthValidityDuration(server, authValidityDuration); | ||
| return key; | ||
| } | ||
| private SecretKey buildCredentialKey(String alias, boolean invalidatedByEnrollment) throws GeneralSecurityException { | ||
| private SecretKey buildCredentialKey(String alias, boolean invalidatedByEnrollment, int authValidityDuration) | ||
| throws GeneralSecurityException { | ||
| KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); | ||
@@ -333,3 +384,5 @@ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder( | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
| builder.setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG); | ||
| builder.setUserAuthenticationParameters(Math.max(0, authValidityDuration), KeyProperties.AUTH_BIOMETRIC_STRONG); | ||
| } else if (authValidityDuration > 0) { | ||
| builder.setUserAuthenticationValidityDurationSeconds(authValidityDuration); | ||
| } else { | ||
@@ -348,2 +401,13 @@ // Use -1 for per-operation authentication, required for BiometricPrompt CryptoObject binding. | ||
| private int getStoredAuthValidityDuration(String server) { | ||
| SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE); | ||
| return prefs.getInt("secure_" + server + SECURE_VALIDITY_SUFFIX, 0); | ||
| } | ||
| private void storeAuthValidityDuration(String server, int authValidityDuration) { | ||
| SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit(); | ||
| editor.putInt("secure_" + server + SECURE_VALIDITY_SUFFIX, authValidityDuration); | ||
| editor.apply(); | ||
| } | ||
| private BiometricPrompt.CryptoObject createCredentialEncryptCryptoObject() { | ||
@@ -404,4 +468,11 @@ try { | ||
| private void handleSetSecureCredentials(BiometricPrompt.AuthenticationResult result) { | ||
| encryptAndStoreCredentials(result.getCryptoObject().getCipher()); | ||
| } | ||
| private void handleGetSecureCredentials(BiometricPrompt.AuthenticationResult result) { | ||
| decryptAndReturnCredentials(result.getCryptoObject().getCipher()); | ||
| } | ||
| private void encryptAndStoreCredentials(Cipher cipher) { | ||
| try { | ||
| Cipher cipher = result.getCryptoObject().getCipher(); | ||
| String username = getIntent().getStringExtra("username"); | ||
@@ -434,5 +505,4 @@ String password = getIntent().getStringExtra("password"); | ||
| private void handleGetSecureCredentials(BiometricPrompt.AuthenticationResult result) { | ||
| private void decryptAndReturnCredentials(Cipher cipher) { | ||
| try { | ||
| Cipher cipher = result.getCryptoObject().getCipher(); | ||
| String server = getIntent().getStringExtra("server"); | ||
@@ -467,2 +537,111 @@ | ||
| /** | ||
| * Validity-window mode (authValidityDuration > 0): attempt the Keystore cipher operation | ||
| * directly, with no BiometricPrompt. If a prior authentication is still within the window, | ||
| * this succeeds immediately and the credential read/write finishes without ever prompting | ||
| * the user. Returns true if the activity was finished this way (success or a non-auth | ||
| * error); returns false if a BiometricPrompt is required (window expired or never started), | ||
| * in which case the caller should continue with the prompt-without-CryptoObject flow. | ||
| */ | ||
| private boolean tryWithoutPrompt() { | ||
| try { | ||
| if ("setSecureCredentials".equals(mode)) { | ||
| String server = getIntent().getStringExtra("server"); | ||
| int accessControl = getIntent().getIntExtra("accessControl", 2); | ||
| Cipher cipher = createCredentialCipherForEncrypt(server, accessControl, authValidityDuration); | ||
| encryptAndStoreCredentials(cipher); | ||
| } else { | ||
| String server = getIntent().getStringExtra("server"); | ||
| Cipher cipher = createCredentialCipherForDecrypt(server, authValidityDuration); | ||
| if (cipher == null) { | ||
| finishActivity("error", 21, "No protected credentials found"); | ||
| return true; | ||
| } | ||
| decryptAndReturnCredentials(cipher); | ||
| } | ||
| return true; | ||
| } catch (UserNotAuthenticatedException e) { | ||
| // Validity window expired (or this is the first use) — a prompt is required. | ||
| return false; | ||
| } catch (GeneralSecurityException | IOException e) { | ||
| finishActivity("error", 0, "Keystore operation failed: " + e.getMessage()); | ||
| return true; | ||
| } | ||
| } | ||
| /** | ||
| * Called after a successful BiometricPrompt shown without a CryptoObject (validity-window | ||
| * mode). The authentication just performed unlocks the Keystore key for the window, so the | ||
| * same cipher operation that threw UserNotAuthenticatedException in tryWithoutPrompt() is | ||
| * now expected to succeed. | ||
| */ | ||
| private void retryAfterPrompt() { | ||
| try { | ||
| if ("setSecureCredentials".equals(mode)) { | ||
| String server = getIntent().getStringExtra("server"); | ||
| int accessControl = getIntent().getIntExtra("accessControl", 2); | ||
| Cipher cipher = createCredentialCipherForEncrypt(server, accessControl, authValidityDuration); | ||
| encryptAndStoreCredentials(cipher); | ||
| } else { | ||
| String server = getIntent().getStringExtra("server"); | ||
| Cipher cipher = createCredentialCipherForDecrypt(server, authValidityDuration); | ||
| if (cipher == null) { | ||
| finishActivity("error", 21, "No protected credentials found"); | ||
| return; | ||
| } | ||
| decryptAndReturnCredentials(cipher); | ||
| } | ||
| } catch (GeneralSecurityException | IOException e) { | ||
| finishActivity("error", 0, "Keystore operation failed after authentication: " + e.getMessage()); | ||
| } | ||
| } | ||
| private Cipher createCredentialCipherForEncrypt(String server, int accessControl, int authValidityDuration) | ||
| throws GeneralSecurityException, IOException { | ||
| SecretKey key = getOrCreateCredentialKey(server, accessControl, authValidityDuration); | ||
| Cipher cipher = Cipher.getInstance(AUTH_TRANSFORMATION); | ||
| try { | ||
| cipher.init(Cipher.ENCRYPT_MODE, key); | ||
| } catch (InvalidKeyException e) { | ||
| if (e instanceof UserNotAuthenticatedException) { | ||
| throw e; | ||
| } | ||
| // KeyPermanentlyInvalidatedException (biometric enrollment changed) — delete and recreate. | ||
| KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); | ||
| ks.load(null); | ||
| ks.deleteEntry(SECURE_KEY_PREFIX + server); | ||
| key = getOrCreateCredentialKey(server, accessControl, authValidityDuration); | ||
| cipher.init(Cipher.ENCRYPT_MODE, key); | ||
| } | ||
| return cipher; | ||
| } | ||
| private Cipher createCredentialCipherForDecrypt(String server, int authValidityDuration) throws GeneralSecurityException, IOException { | ||
| SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE); | ||
| String encryptedData = prefs.getString("secure_" + server, null); | ||
| if (encryptedData == null) return null; | ||
| byte[] combined = Base64.decode(encryptedData, Base64.DEFAULT); | ||
| byte[] iv = new byte[CREDENTIAL_GCM_IV_LENGTH]; | ||
| System.arraycopy(combined, 0, iv, 0, CREDENTIAL_GCM_IV_LENGTH); | ||
| SecretKey key = getOrCreateCredentialKey(server, 0, authValidityDuration); | ||
| Cipher cipher = Cipher.getInstance(AUTH_TRANSFORMATION); | ||
| try { | ||
| cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv)); | ||
| } catch (InvalidKeyException e) { | ||
| if (e instanceof UserNotAuthenticatedException) { | ||
| throw e; | ||
| } | ||
| // Key was invalidated (e.g. biometric enrollment changed). Delete the unusable key | ||
| // and encrypted data so the user can re-enroll via setSecureCredentials. | ||
| KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); | ||
| ks.load(null); | ||
| ks.deleteEntry(SECURE_KEY_PREFIX + server); | ||
| prefs.edit().remove("secure_" + server).apply(); | ||
| return null; | ||
| } | ||
| return cipher; | ||
| } | ||
| /** | ||
| * Convert Auth Error Codes to plugin expected Biometric Auth Errors (in README.md) | ||
@@ -469,0 +648,0 @@ * This way both iOS and Android return the same error codes for the same authentication failure reasons. |
@@ -273,2 +273,3 @@ package ee.forgr.biometric; | ||
| Integer accessControl = call.getInt("accessControl", 0); | ||
| Integer authValidityDuration = call.getInt("authValidityDuration", 0); | ||
@@ -287,2 +288,4 @@ if (username == null || password == null || KEY_ALIAS == null) { | ||
| intent.putExtra("accessControl", accessControl); | ||
| intent.putExtra("authValidityDuration", authValidityDuration != null ? authValidityDuration : 0); | ||
| String title = call.getString("title", "Protect Credentials"); | ||
@@ -445,2 +448,3 @@ if (title == null || title.trim().isEmpty()) { | ||
| editor.remove("secure_" + KEY_ALIAS); | ||
| editor.remove("secure_" + KEY_ALIAS + "_validity"); | ||
| editor.apply(); | ||
@@ -447,0 +451,0 @@ |
+16
-0
@@ -582,2 +582,18 @@ { | ||
| { | ||
| "name": "authValidityDuration", | ||
| "tags": [ | ||
| { | ||
| "text": "0", | ||
| "name": "default" | ||
| }, | ||
| { | ||
| "text": "8.5.0", | ||
| "name": "since" | ||
| } | ||
| ], | ||
| "docs": "Only for Android. Ignored on iOS and web.\nOnly meaningful together with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY.\n\nNumber of seconds a successful biometric authentication remains valid for\nKeystore key use. When `0` or omitted (the default), the Keystore key requires\na fresh authentication for every operation — each `getSecureCredentials()` call\nshows a `BiometricPrompt` cryptographically bound to that specific read via a\n`CryptoObject`. This is the strongest mode: it guarantees no code path can use\nthe key without a live biometric check.\n\nWhen set to a value greater than `0`, one successful biometric authentication\nauthorizes Keystore key use for that many seconds. Subsequent\n`getSecureCredentials()` calls within the window succeed without showing a\nprompt. This trades security for convenience: any in-app code that can reach\nthe decryption call — not just the code path that triggered the prompt — can\nsilently read the credentials for the remainder of the window.", | ||
| "complexTypes": [], | ||
| "type": "number | undefined" | ||
| }, | ||
| { | ||
| "name": "title", | ||
@@ -584,0 +600,0 @@ "tags": [ |
@@ -161,2 +161,24 @@ import type { PluginListenerHandle } from '@capacitor/core'; | ||
| /** | ||
| * Only for Android. Ignored on iOS and web. | ||
| * Only meaningful together with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY. | ||
| * | ||
| * Number of seconds a successful biometric authentication remains valid for | ||
| * Keystore key use. When `0` or omitted (the default), the Keystore key requires | ||
| * a fresh authentication for every operation — each `getSecureCredentials()` call | ||
| * shows a `BiometricPrompt` cryptographically bound to that specific read via a | ||
| * `CryptoObject`. This is the strongest mode: it guarantees no code path can use | ||
| * the key without a live biometric check. | ||
| * | ||
| * When set to a value greater than `0`, one successful biometric authentication | ||
| * authorizes Keystore key use for that many seconds. Subsequent | ||
| * `getSecureCredentials()` calls within the window succeed without showing a | ||
| * prompt. This trades security for convenience: any in-app code that can reach | ||
| * the decryption call — not just the code path that triggered the prompt — can | ||
| * silently read the credentials for the remainder of the window. | ||
| * | ||
| * @default 0 | ||
| * @since 8.5.0 | ||
| */ | ||
| authValidityDuration?: number; | ||
| /** | ||
| * Title for the biometric prompt shown while protecting credentials. | ||
@@ -163,0 +185,0 @@ * Only for Android. |
@@ -1,1 +0,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;AAED,MAAM,CAAN,IAAY,aAkBX;AAlBD,WAAY,aAAa;IACvB;;;OAGG;IACH,iDAAQ,CAAA;IACR;;;;OAIG;IACH,iFAAwB,CAAA;IACxB;;;;OAIG;IACH,iEAAgB,CAAA;AAClB,CAAC,EAlBW,aAAa,KAAb,aAAa,QAkBxB;AAmLD;;;;;;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 enum AccessControl {\n /**\n * No biometric protection. Credentials are accessible without authentication.\n * This is the default behavior for backward compatibility.\n */\n NONE = 0,\n /**\n * Biometric authentication required for credential access.\n * Credentials are invalidated if biometrics change (e.g., new fingerprint enrolled).\n * More secure but credentials are lost if user modifies their biometric enrollment.\n */\n BIOMETRY_CURRENT_SET = 1,\n /**\n * Biometric authentication required for credential access.\n * Credentials survive new biometric enrollment (e.g., adding a new fingerprint).\n * More lenient — recommended for most apps.\n */\n BIOMETRY_ANY = 2,\n}\n\nexport interface Credentials {\n username: string;\n password: string;\n}\n\nexport interface IsAvailableOptions {\n /**\n * Whether passcode or device credentials should count toward biometric availability\n * when no biometric is enrolled or available.\n *\n * - On iOS, this affects both `isAvailable()` and `verifyIdentity()`.\n * - On Android, this is honored by `isAvailable()` only — the native check computes\n * `fallbackAvailable = useFallback && deviceIsSecure` and reports availability\n * accordingly. The `verifyIdentity()` flow ignores this option on Android due to\n * BiometricPrompt API constraints (DEVICE_CREDENTIAL authenticator and negative\n * button are mutually exclusive); use `BiometricOptions.useFallback` (iOS-only) to\n * control the auth-dialog fallback there.\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.\n *\n * On Android, `verifyIdentity()` uses a `CryptoObject` backed by\n * `BIOMETRIC_STRONG`, so weak-only biometric methods such as some face unlock\n * implementations are reported via `biometryType`/`authenticationStrength`\n * but do not make this value `true`.\n * If `useFallback` is true, PIN/pattern/password can make this value 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 * Access control level for the stored credentials.\n * When set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the credentials are\n * hardware-protected and require biometric authentication to access.\n *\n * On iOS, this adds SecAccessControl to the Keychain item.\n * On Android, this creates a biometric-protected Keystore key and requires\n * BiometricPrompt authentication for both storing and retrieving credentials.\n *\n * @default AccessControl.NONE\n * @since 8.4.0\n */\n accessControl?: AccessControl;\n /**\n * Title for the biometric prompt shown while protecting credentials.\n * Only for Android.\n *\n * @default \"Protect Credentials\"\n * @since 8.4.14\n */\n title?: string;\n /**\n * Text for the negative/cancel button in the biometric prompt.\n * Only for Android.\n *\n * @default \"Cancel\"\n * @since 8.4.14\n */\n negativeButtonText?: string;\n}\n\nexport interface GetSecureCredentialsOptions {\n server: string;\n /**\n * Reason for requesting biometric authentication.\n * Displayed in the biometric prompt on both iOS and Android.\n */\n reason?: string;\n /**\n * Title for the biometric prompt.\n * Only for Android.\n */\n title?: string;\n /**\n * Subtitle for the biometric prompt.\n * Only for Android.\n */\n subtitle?: string;\n /**\n * Description for the biometric prompt.\n * Only for Android.\n */\n description?: string;\n /**\n * Text for the negative/cancel button.\n * Only for Android.\n */\n negativeButtonText?: 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 * Gets the stored credentials for a given server, requiring biometric authentication.\n * Credentials must have been stored with accessControl set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY.\n *\n * On iOS, the system automatically shows the biometric prompt when accessing the protected Keychain item.\n * On Android, BiometricPrompt is shown with a CryptoObject bound to the credential decryption key.\n *\n * @param {GetSecureCredentialsOptions} options\n * @returns {Promise<Credentials>}\n * @memberof NativeBiometricPlugin\n * @since 8.4.0\n */\n getSecureCredentials(options: GetSecureCredentialsOptions): Promise<Credentials>;\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"]} | ||
| {"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;AAED,MAAM,CAAN,IAAY,aAkBX;AAlBD,WAAY,aAAa;IACvB;;;OAGG;IACH,iDAAQ,CAAA;IACR;;;;OAIG;IACH,iFAAwB,CAAA;IACxB;;;;OAIG;IACH,iEAAgB,CAAA;AAClB,CAAC,EAlBW,aAAa,KAAb,aAAa,QAkBxB;AAyMD;;;;;;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 enum AccessControl {\n /**\n * No biometric protection. Credentials are accessible without authentication.\n * This is the default behavior for backward compatibility.\n */\n NONE = 0,\n /**\n * Biometric authentication required for credential access.\n * Credentials are invalidated if biometrics change (e.g., new fingerprint enrolled).\n * More secure but credentials are lost if user modifies their biometric enrollment.\n */\n BIOMETRY_CURRENT_SET = 1,\n /**\n * Biometric authentication required for credential access.\n * Credentials survive new biometric enrollment (e.g., adding a new fingerprint).\n * More lenient — recommended for most apps.\n */\n BIOMETRY_ANY = 2,\n}\n\nexport interface Credentials {\n username: string;\n password: string;\n}\n\nexport interface IsAvailableOptions {\n /**\n * Whether passcode or device credentials should count toward biometric availability\n * when no biometric is enrolled or available.\n *\n * - On iOS, this affects both `isAvailable()` and `verifyIdentity()`.\n * - On Android, this is honored by `isAvailable()` only — the native check computes\n * `fallbackAvailable = useFallback && deviceIsSecure` and reports availability\n * accordingly. The `verifyIdentity()` flow ignores this option on Android due to\n * BiometricPrompt API constraints (DEVICE_CREDENTIAL authenticator and negative\n * button are mutually exclusive); use `BiometricOptions.useFallback` (iOS-only) to\n * control the auth-dialog fallback there.\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.\n *\n * On Android, `verifyIdentity()` uses a `CryptoObject` backed by\n * `BIOMETRIC_STRONG`, so weak-only biometric methods such as some face unlock\n * implementations are reported via `biometryType`/`authenticationStrength`\n * but do not make this value `true`.\n * If `useFallback` is true, PIN/pattern/password can make this value 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 * Access control level for the stored credentials.\n * When set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the credentials are\n * hardware-protected and require biometric authentication to access.\n *\n * On iOS, this adds SecAccessControl to the Keychain item.\n * On Android, this creates a biometric-protected Keystore key and requires\n * BiometricPrompt authentication for both storing and retrieving credentials.\n *\n * @default AccessControl.NONE\n * @since 8.4.0\n */\n accessControl?: AccessControl;\n /**\n * Only for Android. Ignored on iOS and web.\n * Only meaningful together with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY.\n *\n * Number of seconds a successful biometric authentication remains valid for\n * Keystore key use. When `0` or omitted (the default), the Keystore key requires\n * a fresh authentication for every operation — each `getSecureCredentials()` call\n * shows a `BiometricPrompt` cryptographically bound to that specific read via a\n * `CryptoObject`. This is the strongest mode: it guarantees no code path can use\n * the key without a live biometric check.\n *\n * When set to a value greater than `0`, one successful biometric authentication\n * authorizes Keystore key use for that many seconds. Subsequent\n * `getSecureCredentials()` calls within the window succeed without showing a\n * prompt. This trades security for convenience: any in-app code that can reach\n * the decryption call — not just the code path that triggered the prompt — can\n * silently read the credentials for the remainder of the window.\n *\n * @default 0\n * @since 8.5.0\n */\n authValidityDuration?: number;\n /**\n * Title for the biometric prompt shown while protecting credentials.\n * Only for Android.\n *\n * @default \"Protect Credentials\"\n * @since 8.4.14\n */\n title?: string;\n /**\n * Text for the negative/cancel button in the biometric prompt.\n * Only for Android.\n *\n * @default \"Cancel\"\n * @since 8.4.14\n */\n negativeButtonText?: string;\n}\n\nexport interface GetSecureCredentialsOptions {\n server: string;\n /**\n * Reason for requesting biometric authentication.\n * Displayed in the biometric prompt on both iOS and Android.\n */\n reason?: string;\n /**\n * Title for the biometric prompt.\n * Only for Android.\n */\n title?: string;\n /**\n * Subtitle for the biometric prompt.\n * Only for Android.\n */\n subtitle?: string;\n /**\n * Description for the biometric prompt.\n * Only for Android.\n */\n description?: string;\n /**\n * Text for the negative/cancel button.\n * Only for Android.\n */\n negativeButtonText?: 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 * Gets the stored credentials for a given server, requiring biometric authentication.\n * Credentials must have been stored with accessControl set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY.\n *\n * On iOS, the system automatically shows the biometric prompt when accessing the protected Keychain item.\n * On Android, BiometricPrompt is shown with a CryptoObject bound to the credential decryption key.\n *\n * @param {GetSecureCredentialsOptions} options\n * @returns {Promise<Credentials>}\n * @memberof NativeBiometricPlugin\n * @since 8.4.0\n */\n getSecureCredentials(options: GetSecureCredentialsOptions): Promise<Credentials>;\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"]} |
@@ -14,3 +14,3 @@ import Foundation | ||
| public class NativeBiometricPlugin: CAPPlugin, CAPBridgedPlugin { | ||
| private let pluginVersion: String = "8.4.14" | ||
| private let pluginVersion: String = "8.5.0" | ||
| public let identifier = "NativeBiometricPlugin" | ||
@@ -17,0 +17,0 @@ public let jsName = "NativeBiometric" |
+1
-1
| { | ||
| "name": "@capgo/capacitor-native-biometric", | ||
| "version": "8.4.14", | ||
| "version": "8.5.0", | ||
| "description": "This plugin gives access to the native biometric apis for android and iOS", | ||
@@ -5,0 +5,0 @@ "main": "dist/plugin.cjs.js", |
+9
-8
@@ -543,10 +543,11 @@ # Capacitor Native Biometric | ||
| | Prop | Type | Description | Default | Since | | ||
| | ------------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ------ | | ||
| | **`username`** | <code>string</code> | | | | | ||
| | **`password`** | <code>string</code> | | | | | ||
| | **`server`** | <code>string</code> | | | | | ||
| | **`accessControl`** | <code><a href="#accesscontrol">AccessControl</a></code> | Access control level for the stored credentials. When set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the credentials are hardware-protected and require biometric authentication to access. On iOS, this adds SecAccessControl to the Keychain item. On Android, this creates a biometric-protected Keystore key and requires BiometricPrompt authentication for both storing and retrieving credentials. | <code>AccessControl.NONE</code> | 8.4.0 | | ||
| | **`title`** | <code>string</code> | Title for the biometric prompt shown while protecting credentials. Only for Android. | <code>"Protect Credentials"</code> | 8.4.14 | | ||
| | **`negativeButtonText`** | <code>string</code> | Text for the negative/cancel button in the biometric prompt. Only for Android. | <code>"Cancel"</code> | 8.4.14 | | ||
| | Prop | Type | Description | Default | Since | | ||
| | -------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ------ | | ||
| | **`username`** | <code>string</code> | | | | | ||
| | **`password`** | <code>string</code> | | | | | ||
| | **`server`** | <code>string</code> | | | | | ||
| | **`accessControl`** | <code><a href="#accesscontrol">AccessControl</a></code> | Access control level for the stored credentials. When set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the credentials are hardware-protected and require biometric authentication to access. On iOS, this adds SecAccessControl to the Keychain item. On Android, this creates a biometric-protected Keystore key and requires BiometricPrompt authentication for both storing and retrieving credentials. | <code>AccessControl.NONE</code> | 8.4.0 | | ||
| | **`authValidityDuration`** | <code>number</code> | Only for Android. Ignored on iOS and web. Only meaningful together with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY. Number of seconds a successful biometric authentication remains valid for Keystore key use. When `0` or omitted (the default), the Keystore key requires a fresh authentication for every operation — each `getSecureCredentials()` call shows a `BiometricPrompt` cryptographically bound to that specific read via a `CryptoObject`. This is the strongest mode: it guarantees no code path can use the key without a live biometric check. When set to a value greater than `0`, one successful biometric authentication authorizes Keystore key use for that many seconds. Subsequent `getSecureCredentials()` calls within the window succeed without showing a prompt. This trades security for convenience: any in-app code that can reach the decryption call — not just the code path that triggered the prompt — can silently read the credentials for the remainder of the window. | <code>0</code> | 8.5.0 | | ||
| | **`title`** | <code>string</code> | Title for the biometric prompt shown while protecting credentials. Only for Android. | <code>"Protect Credentials"</code> | 8.4.14 | | ||
| | **`negativeButtonText`** | <code>string</code> | Text for the negative/cancel button in the biometric prompt. Only for Android. | <code>"Cancel"</code> | 8.4.14 | | ||
@@ -553,0 +554,0 @@ |
284691
7.31%3280
6.81%699
0.14%