🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@capgo/capacitor-native-biometric

Package Overview
Dependencies
Maintainers
1
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@capgo/capacitor-native-biometric - npm Package Compare versions

Comparing version
8.4.14
to
8.5.0
+191
-12
android/src/main/java/ee/forgr/biometric/AuthActivity.java

@@ -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 @@

@@ -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

@@ -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"

{
"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",

@@ -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 @@