@capgo/capacitor-native-biometric
Advanced tools
| package ee.forgr.biometric; | ||
| import android.os.Build; | ||
| import android.security.keystore.KeyProperties; | ||
| import androidx.biometric.BiometricManager; | ||
| /** | ||
| * Maps plugin {@code allowedBiometryTypes} values to Android BiometricPrompt authenticators | ||
| * and matching Keystore user-authentication requirements. | ||
| */ | ||
| public final class BiometricAuthenticatorConfig { | ||
| private static final int FINGERPRINT = 3; | ||
| private static final int FACE_AUTHENTICATION = 4; | ||
| private static final int IRIS_AUTHENTICATION = 5; | ||
| private static final int MULTIPLE = 6; | ||
| private static final int DEVICE_CREDENTIAL = 7; | ||
| // Mirrors KeyProperties auth-type flags when older compile stubs omit symbols. | ||
| private static final int KEY_AUTH_BIOMETRIC_STRONG = 1; | ||
| private static final int KEY_AUTH_BIOMETRIC_WEAK = 2; | ||
| private static final int KEY_AUTH_DEVICE_CREDENTIAL = 4; | ||
| public static final int PROMPT_BIOMETRIC_ANY = | ||
| BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.BIOMETRIC_WEAK; | ||
| public final int promptAuthenticators; | ||
| public final int keyAuthTypes; | ||
| public final boolean allowNegativeButton; | ||
| public final boolean requiresCryptoObject; | ||
| BiometricAuthenticatorConfig(int promptAuthenticators, int keyAuthTypes, boolean allowNegativeButton, boolean requiresCryptoObject) { | ||
| this.promptAuthenticators = promptAuthenticators; | ||
| this.keyAuthTypes = keyAuthTypes; | ||
| this.allowNegativeButton = allowNegativeButton; | ||
| this.requiresCryptoObject = requiresCryptoObject; | ||
| } | ||
| public static BiometricAuthenticatorConfig fromAllowedTypes(int[] allowedTypes) { | ||
| if (allowedTypes == null || allowedTypes.length == 0) { | ||
| return defaultBiometric(); | ||
| } | ||
| int promptAuth = 0; | ||
| int keyAuth = 0; | ||
| boolean hasBiometric = false; | ||
| boolean hasDeviceCredential = false; | ||
| boolean fingerprintOnly = true; | ||
| for (int type : allowedTypes) { | ||
| switch (type) { | ||
| case FINGERPRINT: | ||
| promptAuth |= BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||
| keyAuth |= keyAuthStrong(); | ||
| hasBiometric = true; | ||
| break; | ||
| case FACE_AUTHENTICATION: | ||
| case IRIS_AUTHENTICATION: | ||
| promptAuth |= PROMPT_BIOMETRIC_ANY; | ||
| keyAuth |= keyAuthAny(); | ||
| hasBiometric = true; | ||
| fingerprintOnly = false; | ||
| break; | ||
| case MULTIPLE: | ||
| promptAuth |= PROMPT_BIOMETRIC_ANY; | ||
| keyAuth |= keyAuthAny(); | ||
| hasBiometric = true; | ||
| fingerprintOnly = false; | ||
| break; | ||
| case DEVICE_CREDENTIAL: | ||
| promptAuth |= BiometricManager.Authenticators.DEVICE_CREDENTIAL; | ||
| keyAuth |= keyAuthDeviceCredential(); | ||
| hasDeviceCredential = true; | ||
| fingerprintOnly = false; | ||
| break; | ||
| default: | ||
| // Ignore iOS-only enum values (TOUCH_ID, FACE_ID). | ||
| break; | ||
| } | ||
| } | ||
| if (promptAuth == 0) { | ||
| return defaultBiometric(); | ||
| } | ||
| if (hasBiometric && fingerprintOnly && !hasDeviceCredential) { | ||
| promptAuth = BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||
| keyAuth = keyAuthStrong(); | ||
| } | ||
| boolean allowNegative = !hasDeviceCredential; | ||
| boolean deviceCredentialOnly = hasDeviceCredential && !hasBiometric; | ||
| return new BiometricAuthenticatorConfig(promptAuth, keyAuth > 0 ? keyAuth : keyAuthAny(), allowNegative, !deviceCredentialOnly); | ||
| } | ||
| private static BiometricAuthenticatorConfig defaultBiometric() { | ||
| return new BiometricAuthenticatorConfig(PROMPT_BIOMETRIC_ANY, keyAuthAny(), true, true); | ||
| } | ||
| private static int keyAuthStrong() { | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
| return KEY_AUTH_BIOMETRIC_STRONG; | ||
| } | ||
| return 0; | ||
| } | ||
| private static int keyAuthAny() { | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { | ||
| return KEY_AUTH_BIOMETRIC_STRONG | KEY_AUTH_BIOMETRIC_WEAK; | ||
| } | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
| return KEY_AUTH_BIOMETRIC_STRONG; | ||
| } | ||
| return 0; | ||
| } | ||
| private static int keyAuthDeviceCredential() { | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
| return KEY_AUTH_DEVICE_CREDENTIAL; | ||
| } | ||
| return 0; | ||
| } | ||
| } |
@@ -51,3 +51,27 @@ package ee.forgr.biometric; | ||
| private int authValidityDuration; | ||
| private BiometricAuthenticatorConfig authenticatorConfig; | ||
| private static final String AUTH_KEY_AUTH_TYPES = "auth_key_auth_types"; | ||
| // Mirrors KeyProperties auth-type flags when older compile stubs omit symbols. | ||
| private static final int KEY_AUTH_BIOMETRIC_STRONG = 1; | ||
| private static final int KEY_AUTH_BIOMETRIC_WEAK = 2; | ||
| private static final int KEY_AUTH_DEVICE_CREDENTIAL = 4; | ||
| private boolean isSecureStorageMode() { | ||
| return ( | ||
| "setSecureCredentials".equals(mode) || | ||
| "getSecureCredentials".equals(mode) || | ||
| "setSecureData".equals(mode) || | ||
| "getSecureData".equals(mode) | ||
| ); | ||
| } | ||
| private boolean isSecureWriteMode() { | ||
| return "setSecureCredentials".equals(mode) || "setSecureData".equals(mode); | ||
| } | ||
| private boolean isSecureReadMode() { | ||
| return "getSecureCredentials".equals(mode) || "getSecureData".equals(mode); | ||
| } | ||
| @Override | ||
@@ -65,10 +89,10 @@ protected void onCreate(Bundle savedInstanceState) { | ||
| String server = getIntent().getStringExtra("server"); | ||
| if ("setSecureCredentials".equals(mode)) { | ||
| if ("setSecureCredentials".equals(mode) || "setSecureData".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)) { | ||
| } else if ("getSecureCredentials".equals(mode) || "getSecureData".equals(mode)) { | ||
| authValidityDuration = getStoredAuthValidityDuration(server); | ||
| } | ||
| if (("setSecureCredentials".equals(mode) || "getSecureCredentials".equals(mode)) && authValidityDuration > 0) { | ||
| if (isSecureStorageMode() && authValidityDuration > 0) { | ||
| // Opt-in validity-window mode: try the Keystore operation without a prompt first. | ||
@@ -98,19 +122,11 @@ // If the window already covers us, we can finish immediately with no BiometricPrompt. | ||
| // Note: useFallback parameter is ignored on Android (iOS-only feature) | ||
| // Android's BiometricPrompt API has a constraint: when DEVICE_CREDENTIAL authenticator is used, | ||
| // setNegativeButtonText() cannot be called (it will throw IllegalArgumentException). | ||
| // Since this plugin always provides a cancel button for consistency, we cannot support | ||
| // device credential fallback. Users should use system settings to enroll biometrics instead. | ||
| int[] allowedTypes = getIntent().getIntArrayExtra("allowedBiometryTypes"); | ||
| authenticatorConfig = BiometricAuthenticatorConfig.fromAllowedTypes(allowedTypes); | ||
| builder.setAllowedAuthenticators(authenticatorConfig.promptAuthenticators); | ||
| int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||
| if (allowedTypes != null) { | ||
| // Filter authenticators based on allowed types | ||
| authenticators = getAllowedAuthenticators(allowedTypes); | ||
| if (authenticatorConfig.allowNegativeButton) { | ||
| String negativeText = getIntent().getStringExtra("negativeButtonText"); | ||
| builder.setNegativeButtonText(negativeText != null ? negativeText : "Cancel"); | ||
| } | ||
| builder.setAllowedAuthenticators(authenticators); | ||
| String negativeText = getIntent().getStringExtra("negativeButtonText"); | ||
| builder.setNegativeButtonText(negativeText != null ? negativeText : "Cancel"); | ||
| BiometricPrompt.PromptInfo promptInfo = builder.build(); | ||
@@ -138,4 +154,3 @@ | ||
| super.onAuthenticationSucceeded(result); | ||
| boolean isValidityWindowMode = | ||
| ("setSecureCredentials".equals(mode) || "getSecureCredentials".equals(mode)) && authValidityDuration > 0; | ||
| boolean isValidityWindowMode = isSecureStorageMode() && authValidityDuration > 0; | ||
| if (isValidityWindowMode) { | ||
@@ -146,7 +161,7 @@ // The prompt carries no CryptoObject in this mode (see tryWithoutPrompt) — the | ||
| retryAfterPrompt(); | ||
| } else if ("setSecureCredentials".equals(mode)) { | ||
| } else if ("setSecureCredentials".equals(mode) || "setSecureData".equals(mode)) { | ||
| handleSetSecureCredentials(result); | ||
| } else if ("getSecureCredentials".equals(mode)) { | ||
| } else if ("getSecureCredentials".equals(mode) || "getSecureData".equals(mode)) { | ||
| handleGetSecureCredentials(result); | ||
| } else { | ||
| } else if (authenticatorConfig.requiresCryptoObject) { | ||
| if (!validateCryptoObject(result)) { | ||
@@ -157,2 +172,4 @@ finishActivity("error", 10, "Biometric security check failed"); | ||
| finishActivity(); | ||
| } else { | ||
| finishActivity(); | ||
| } | ||
@@ -174,3 +191,3 @@ } | ||
| if (("setSecureCredentials".equals(mode) || "getSecureCredentials".equals(mode)) && authValidityDuration > 0) { | ||
| if (isSecureStorageMode() && authValidityDuration > 0) { | ||
| // Validity-window mode: a single authentication unlocks the Keystore key for | ||
@@ -182,6 +199,11 @@ // `authValidityDuration` seconds, so the prompt is not bound to a CryptoObject. | ||
| if (!authenticatorConfig.requiresCryptoObject) { | ||
| biometricPrompt.authenticate(promptInfo); | ||
| return; | ||
| } | ||
| BiometricPrompt.CryptoObject cryptoObject; | ||
| if ("setSecureCredentials".equals(mode)) { | ||
| if ("setSecureCredentials".equals(mode) || "setSecureData".equals(mode)) { | ||
| cryptoObject = createCredentialEncryptCryptoObject(); | ||
| } else if ("getSecureCredentials".equals(mode)) { | ||
| } else if ("getSecureCredentials".equals(mode) || "getSecureData".equals(mode)) { | ||
| cryptoObject = createCredentialDecryptCryptoObject(); | ||
@@ -246,4 +268,10 @@ } else { | ||
| } | ||
| int expectedAuthTypes = getAuthKeyTypes(); | ||
| int storedAuthTypes = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).getInt(AUTH_KEY_AUTH_TYPES, -1); | ||
| if (keyStore.containsAlias(AUTH_KEY_ALIAS) && storedAuthTypes != expectedAuthTypes) { | ||
| keyStore.deleteEntry(AUTH_KEY_ALIAS); | ||
| } | ||
| if (!keyStore.containsAlias(AUTH_KEY_ALIAS)) { | ||
| generateSecretKey(); | ||
| storeAuthKeyTypes(expectedAuthTypes); | ||
| } | ||
@@ -259,3 +287,3 @@ try { | ||
| try { | ||
| buildAuthKey(true); | ||
| buildAuthKey(true, getAuthKeyTypes()); | ||
| } catch (ProviderException e) { | ||
@@ -266,3 +294,3 @@ // Some OEM Keymaster/TEE implementations (notably Xiaomi/MIUI and Oppo/ColorOS) reject | ||
| try { | ||
| buildAuthKey(false); | ||
| buildAuthKey(false, getAuthKeyTypes()); | ||
| } catch (ProviderException retryError) { | ||
@@ -276,3 +304,3 @@ // ProviderException is unchecked and would otherwise crash AuthActivity.onCreate. | ||
| private void buildAuthKey(boolean invalidatedByEnrollment) throws GeneralSecurityException { | ||
| private void buildAuthKey(boolean invalidatedByEnrollment, int keyAuthTypes) throws GeneralSecurityException { | ||
| KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); | ||
@@ -288,3 +316,4 @@ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder( | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
| builder.setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG); | ||
| int authTypes = keyAuthTypes > 0 ? keyAuthTypes : defaultKeyAuthTypes(); | ||
| builder.setUserAuthenticationParameters(0, authTypes); | ||
| } else { | ||
@@ -388,3 +417,7 @@ // Use -1 for per-operation authentication, required for BiometricPrompt CryptoObject binding. | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
| builder.setUserAuthenticationParameters(Math.max(0, authValidityDuration), KeyProperties.AUTH_BIOMETRIC_STRONG); | ||
| int authTypes = getAuthKeyTypes(); | ||
| if (authTypes == 0) { | ||
| authTypes = defaultKeyAuthTypes(); | ||
| } | ||
| builder.setUserAuthenticationParameters(Math.max(0, authValidityDuration), authTypes); | ||
| } else if (authValidityDuration > 0) { | ||
@@ -480,11 +513,17 @@ builder.setUserAuthenticationValidityDurationSeconds(authValidityDuration); | ||
| try { | ||
| String username = getIntent().getStringExtra("username"); | ||
| String password = getIntent().getStringExtra("password"); | ||
| String server = getIntent().getStringExtra("server"); | ||
| byte[] plaintext; | ||
| if ("setSecureData".equals(mode)) { | ||
| String value = getIntent().getStringExtra("value"); | ||
| plaintext = value.getBytes(StandardCharsets.UTF_8); | ||
| } else { | ||
| String username = getIntent().getStringExtra("username"); | ||
| String password = getIntent().getStringExtra("password"); | ||
| JSONObject json = new JSONObject(); | ||
| json.put("u", username); | ||
| json.put("p", password); | ||
| plaintext = json.toString().getBytes(StandardCharsets.UTF_8); | ||
| } | ||
| JSONObject json = new JSONObject(); | ||
| json.put("u", username); | ||
| json.put("p", password); | ||
| byte[] encrypted = cipher.doFinal(json.toString().getBytes(StandardCharsets.UTF_8)); | ||
| byte[] encrypted = cipher.doFinal(plaintext); | ||
| byte[] iv = cipher.getIV(); | ||
@@ -524,9 +563,12 @@ | ||
| byte[] decrypted = cipher.doFinal(ciphertext); | ||
| String jsonStr = new String(decrypted, StandardCharsets.UTF_8); | ||
| JSONObject json = new JSONObject(jsonStr); | ||
| Intent intent = new Intent(); | ||
| intent.putExtra("result", "success"); | ||
| intent.putExtra("username", json.getString("u")); | ||
| intent.putExtra("password", json.getString("p")); | ||
| if ("getSecureData".equals(mode)) { | ||
| intent.putExtra("value", new String(decrypted, StandardCharsets.UTF_8)); | ||
| } else { | ||
| String jsonStr = new String(decrypted, StandardCharsets.UTF_8); | ||
| JSONObject json = new JSONObject(jsonStr); | ||
| intent.putExtra("username", json.getString("u")); | ||
| intent.putExtra("password", json.getString("p")); | ||
| } | ||
| setResult(RESULT_OK, intent); | ||
@@ -549,3 +591,3 @@ finish(); | ||
| try { | ||
| if ("setSecureCredentials".equals(mode)) { | ||
| if (isSecureWriteMode()) { | ||
| String server = getIntent().getStringExtra("server"); | ||
@@ -582,3 +624,3 @@ int accessControl = getIntent().getIntExtra("accessControl", 2); | ||
| try { | ||
| if ("setSecureCredentials".equals(mode)) { | ||
| if (isSecureWriteMode()) { | ||
| String server = getIntent().getStringExtra("server"); | ||
@@ -685,25 +727,22 @@ int accessControl = getIntent().getIntExtra("accessControl", 2); | ||
| private int getAllowedAuthenticators(int[] allowedTypes) { | ||
| int authenticators = 0; | ||
| for (int type : allowedTypes) { | ||
| switch (type) { | ||
| case 3: // FINGERPRINT | ||
| authenticators |= BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||
| break; | ||
| case 4: // FACE_AUTHENTICATION | ||
| authenticators |= BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||
| break; | ||
| case 5: // IRIS_AUTHENTICATION | ||
| authenticators |= BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||
| break; | ||
| case 6: // MULTIPLE - allow all biometric types | ||
| authenticators |= BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||
| break; | ||
| case 7: // DEVICE_CREDENTIAL (PIN, pattern, or password) | ||
| authenticators |= BiometricManager.Authenticators.DEVICE_CREDENTIAL; | ||
| break; | ||
| } | ||
| private static int defaultKeyAuthTypes() { | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { | ||
| return KEY_AUTH_BIOMETRIC_STRONG | KEY_AUTH_BIOMETRIC_WEAK; | ||
| } | ||
| return authenticators > 0 ? authenticators : BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
| return KEY_AUTH_BIOMETRIC_STRONG; | ||
| } | ||
| return 0; | ||
| } | ||
| private int getAuthKeyTypes() { | ||
| if (authenticatorConfig != null && authenticatorConfig.keyAuthTypes > 0) { | ||
| return authenticatorConfig.keyAuthTypes; | ||
| } | ||
| return defaultKeyAuthTypes(); | ||
| } | ||
| private void storeAuthKeyTypes(int authTypes) { | ||
| getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit().putInt(AUTH_KEY_AUTH_TYPES, authTypes).apply(); | ||
| } | ||
| } |
@@ -81,2 +81,4 @@ package ee.forgr.biometric; | ||
| private static final String NATIVE_BIOMETRIC_SHARED_PREFERENCES = "NativeBiometricSharedPreferences"; | ||
| private static final String DATA_KEY_PREFIX = "data_"; | ||
| private static final String DATA_KEYSTORE_PREFIX = "NativeBiometricData_"; | ||
@@ -134,5 +136,4 @@ private SharedPreferences encryptedSharedPreferences; | ||
| } else if (hasWeakBiometric) { | ||
| // Weak-only biometrics (e.g. some face unlock) are reported for UX via | ||
| // biometryType/authenticationStrength but do not enable verifyIdentity(). | ||
| authenticationStrength = AUTH_STRENGTH_WEAK; | ||
| isAvailable = true; | ||
| } else if (fallbackAvailable) { | ||
@@ -489,2 +490,201 @@ authenticationStrength = AUTH_STRENGTH_WEAK; | ||
| private String dataStorageKey(String key) { | ||
| return DATA_KEY_PREFIX + key; | ||
| } | ||
| private String dataKeyAlias(String key) { | ||
| return DATA_KEYSTORE_PREFIX + key; | ||
| } | ||
| @PluginMethod | ||
| public void setData(final PluginCall call) { | ||
| String key = call.getString("key", null); | ||
| String value = call.getString("value", null); | ||
| Integer accessControl = call.getInt("accessControl", 0); | ||
| Integer authValidityDuration = call.getInt("authValidityDuration", 0); | ||
| if (key == null || value == null) { | ||
| call.reject("Missing properties"); | ||
| return; | ||
| } | ||
| String storageKey = dataStorageKey(key); | ||
| if (accessControl != null && accessControl > 0) { | ||
| Intent intent = new Intent(getContext(), AuthActivity.class); | ||
| intent.putExtra("mode", "setSecureData"); | ||
| intent.putExtra("server", storageKey); | ||
| intent.putExtra("value", value); | ||
| intent.putExtra("accessControl", accessControl); | ||
| intent.putExtra("authValidityDuration", authValidityDuration != null ? authValidityDuration : 0); | ||
| String title = call.getString("title", "Protect Data"); | ||
| if (title == null || title.trim().isEmpty()) { | ||
| title = "Protect Data"; | ||
| } | ||
| intent.putExtra("title", title); | ||
| String negativeButtonText = call.getString("negativeButtonText", "Cancel"); | ||
| if (negativeButtonText == null || negativeButtonText.trim().isEmpty()) { | ||
| negativeButtonText = "Cancel"; | ||
| } | ||
| intent.putExtra("negativeButtonText", negativeButtonText); | ||
| startActivityForResult(call, intent, "setSecureDataResult"); | ||
| } else { | ||
| try { | ||
| SharedPreferences.Editor editor = getContext() | ||
| .getSharedPreferences(NATIVE_BIOMETRIC_SHARED_PREFERENCES, Context.MODE_PRIVATE) | ||
| .edit(); | ||
| editor.putString(storageKey, encryptString(value, dataKeyAlias(key))); | ||
| editor.apply(); | ||
| call.resolve(); | ||
| } catch (GeneralSecurityException | IOException e) { | ||
| call.reject("Failed to save data", e); | ||
| } | ||
| } | ||
| } | ||
| @PluginMethod | ||
| public void getData(final PluginCall call) { | ||
| String key = call.getString("key", null); | ||
| if (key == null) { | ||
| call.reject("No key was provided"); | ||
| return; | ||
| } | ||
| String storageKey = dataStorageKey(key); | ||
| SharedPreferences sharedPreferences = getContext().getSharedPreferences(NATIVE_BIOMETRIC_SHARED_PREFERENCES, Context.MODE_PRIVATE); | ||
| String encryptedValue = sharedPreferences.getString(storageKey, null); | ||
| if (encryptedValue == null) { | ||
| call.reject("No data found"); | ||
| return; | ||
| } | ||
| try { | ||
| JSObject jsObject = new JSObject(); | ||
| jsObject.put("value", decryptString(encryptedValue, dataKeyAlias(key))); | ||
| call.resolve(jsObject); | ||
| } catch (GeneralSecurityException | IOException e) { | ||
| call.reject("Failed to get data"); | ||
| } | ||
| } | ||
| @PluginMethod | ||
| public void getSecureData(final PluginCall call) { | ||
| String key = call.getString("key", null); | ||
| if (key == null) { | ||
| call.reject("No key was provided"); | ||
| return; | ||
| } | ||
| String storageKey = dataStorageKey(key); | ||
| SharedPreferences sharedPreferences = getContext().getSharedPreferences(NATIVE_BIOMETRIC_SHARED_PREFERENCES, Context.MODE_PRIVATE); | ||
| String encryptedData = sharedPreferences.getString("secure_" + storageKey, null); | ||
| if (encryptedData == null) { | ||
| call.reject("No protected data found", "21"); | ||
| return; | ||
| } | ||
| Intent intent = new Intent(getContext(), AuthActivity.class); | ||
| intent.putExtra("mode", "getSecureData"); | ||
| intent.putExtra("server", storageKey); | ||
| intent.putExtra("title", call.getString("title", "Authenticate")); | ||
| String subtitle = call.getString("subtitle"); | ||
| if (subtitle != null) intent.putExtra("subtitle", subtitle); | ||
| String description = call.getString("description"); | ||
| if (description != null) intent.putExtra("description", description); | ||
| String negativeText = call.getString("negativeButtonText"); | ||
| if (negativeText != null) intent.putExtra("negativeButtonText", negativeText); | ||
| startActivityForResult(call, intent, "getSecureDataResult"); | ||
| } | ||
| @PluginMethod | ||
| public void deleteData(final PluginCall call) { | ||
| String key = call.getString("key", null); | ||
| if (key == null) { | ||
| call.reject("No key was provided"); | ||
| return; | ||
| } | ||
| String storageKey = dataStorageKey(key); | ||
| String keyAlias = dataKeyAlias(key); | ||
| try { | ||
| getKeyStore().deleteEntry(keyAlias); | ||
| SharedPreferences.Editor editor = getContext() | ||
| .getSharedPreferences(NATIVE_BIOMETRIC_SHARED_PREFERENCES, Context.MODE_PRIVATE) | ||
| .edit(); | ||
| editor.remove(storageKey); | ||
| editor.remove("secure_" + storageKey); | ||
| editor.remove("secure_" + storageKey + "_validity"); | ||
| editor.apply(); | ||
| try { | ||
| getKeyStore().deleteEntry("NativeBiometricSecure_" + storageKey); | ||
| } catch (KeyStoreException e) { | ||
| // Ignore — may not exist | ||
| } | ||
| call.resolve(); | ||
| } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) { | ||
| call.reject("Failed to delete data", e); | ||
| } | ||
| } | ||
| @PluginMethod | ||
| public void isDataSaved(final PluginCall call) { | ||
| String key = call.getString("key", null); | ||
| if (key == null) { | ||
| call.reject("No key was provided"); | ||
| return; | ||
| } | ||
| String storageKey = dataStorageKey(key); | ||
| SharedPreferences sharedPreferences = getContext().getSharedPreferences(NATIVE_BIOMETRIC_SHARED_PREFERENCES, Context.MODE_PRIVATE); | ||
| boolean hasUnprotected = sharedPreferences.getString(storageKey, null) != null; | ||
| boolean hasProtected = sharedPreferences.getString("secure_" + storageKey, null) != null; | ||
| JSObject ret = new JSObject(); | ||
| ret.put("isSaved", hasUnprotected || hasProtected); | ||
| call.resolve(ret); | ||
| } | ||
| @ActivityCallback | ||
| private void setSecureDataResult(PluginCall call, ActivityResult result) { | ||
| if (result.getResultCode() == Activity.RESULT_OK) { | ||
| Intent data = result.getData(); | ||
| if (data != null && "success".equals(data.getStringExtra("result"))) { | ||
| call.resolve(); | ||
| } else { | ||
| String errorCode = data != null ? data.getStringExtra("errorCode") : "0"; | ||
| String errorDetails = data != null ? data.getStringExtra("errorDetails") : "Failed to store data"; | ||
| call.reject(errorDetails, errorCode); | ||
| } | ||
| } else { | ||
| call.reject("Failed to store data"); | ||
| } | ||
| } | ||
| @ActivityCallback | ||
| private void getSecureDataResult(PluginCall call, ActivityResult result) { | ||
| if (result.getResultCode() == Activity.RESULT_OK) { | ||
| Intent data = result.getData(); | ||
| if (data != null && "success".equals(data.getStringExtra("result"))) { | ||
| JSObject jsObject = new JSObject(); | ||
| jsObject.put("value", data.getStringExtra("value")); | ||
| call.resolve(jsObject); | ||
| } else { | ||
| String errorCode = data != null ? data.getStringExtra("errorCode") : "0"; | ||
| String errorDetails = data != null ? data.getStringExtra("errorDetails") : "Authentication failed"; | ||
| call.reject(errorDetails, errorCode); | ||
| } | ||
| } else { | ||
| call.reject("Authentication failed"); | ||
| } | ||
| } | ||
| private String encryptString(String stringToEncrypt, String KEY_ALIAS) throws GeneralSecurityException, IOException { | ||
@@ -491,0 +691,0 @@ Cipher cipher; |
+395
-2
@@ -296,2 +296,175 @@ { | ||
| { | ||
| "name": "setData", | ||
| "signature": "(options: SetDataOptions) => Promise<void>", | ||
| "parameters": [ | ||
| { | ||
| "name": "options", | ||
| "docs": "", | ||
| "type": "SetDataOptions" | ||
| } | ||
| ], | ||
| "returns": "Promise<void>", | ||
| "tags": [ | ||
| { | ||
| "name": "param", | ||
| "text": "options" | ||
| }, | ||
| { | ||
| "name": "returns" | ||
| }, | ||
| { | ||
| "name": "memberof", | ||
| "text": "NativeBiometricPlugin" | ||
| }, | ||
| { | ||
| "name": "since", | ||
| "text": "8.6.0" | ||
| } | ||
| ], | ||
| "docs": "Stores an arbitrary string value under the given key.\nValues are encrypted at rest using the platform secure storage backend\n(Android Keystore + SharedPreferences, iOS Keychain).\n\nFor biometric-protected storage, set `accessControl` and retrieve the value\nwith `getSecureData()`. Credential helpers remain available for username/password flows.", | ||
| "complexTypes": [ | ||
| "SetDataOptions" | ||
| ], | ||
| "slug": "setdata" | ||
| }, | ||
| { | ||
| "name": "getData", | ||
| "signature": "(options: GetDataOptions) => Promise<StoredData>", | ||
| "parameters": [ | ||
| { | ||
| "name": "options", | ||
| "docs": "", | ||
| "type": "GetDataOptions" | ||
| } | ||
| ], | ||
| "returns": "Promise<StoredData>", | ||
| "tags": [ | ||
| { | ||
| "name": "param", | ||
| "text": "options" | ||
| }, | ||
| { | ||
| "name": "returns" | ||
| }, | ||
| { | ||
| "name": "memberof", | ||
| "text": "NativeBiometricPlugin" | ||
| }, | ||
| { | ||
| "name": "since", | ||
| "text": "8.6.0" | ||
| } | ||
| ], | ||
| "docs": "Gets a previously stored value for the given key.\nOnly returns values stored without biometric `accessControl`.", | ||
| "complexTypes": [ | ||
| "StoredData", | ||
| "GetDataOptions" | ||
| ], | ||
| "slug": "getdata" | ||
| }, | ||
| { | ||
| "name": "getSecureData", | ||
| "signature": "(options: GetSecureDataOptions) => Promise<StoredData>", | ||
| "parameters": [ | ||
| { | ||
| "name": "options", | ||
| "docs": "", | ||
| "type": "GetSecureDataOptions" | ||
| } | ||
| ], | ||
| "returns": "Promise<StoredData>", | ||
| "tags": [ | ||
| { | ||
| "name": "param", | ||
| "text": "options" | ||
| }, | ||
| { | ||
| "name": "returns" | ||
| }, | ||
| { | ||
| "name": "memberof", | ||
| "text": "NativeBiometricPlugin" | ||
| }, | ||
| { | ||
| "name": "since", | ||
| "text": "8.6.0" | ||
| } | ||
| ], | ||
| "docs": "Gets a biometric-protected value for the given key.\nThe value must have been stored with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY.", | ||
| "complexTypes": [ | ||
| "StoredData", | ||
| "GetSecureDataOptions" | ||
| ], | ||
| "slug": "getsecuredata" | ||
| }, | ||
| { | ||
| "name": "deleteData", | ||
| "signature": "(options: DeleteDataOptions) => Promise<void>", | ||
| "parameters": [ | ||
| { | ||
| "name": "options", | ||
| "docs": "", | ||
| "type": "DeleteDataOptions" | ||
| } | ||
| ], | ||
| "returns": "Promise<void>", | ||
| "tags": [ | ||
| { | ||
| "name": "param", | ||
| "text": "options" | ||
| }, | ||
| { | ||
| "name": "returns" | ||
| }, | ||
| { | ||
| "name": "memberof", | ||
| "text": "NativeBiometricPlugin" | ||
| }, | ||
| { | ||
| "name": "since", | ||
| "text": "8.6.0" | ||
| } | ||
| ], | ||
| "docs": "Deletes the stored value for the given key (protected and unprotected).", | ||
| "complexTypes": [ | ||
| "DeleteDataOptions" | ||
| ], | ||
| "slug": "deletedata" | ||
| }, | ||
| { | ||
| "name": "isDataSaved", | ||
| "signature": "(options: IsDataSavedOptions) => Promise<IsDataSavedResult>", | ||
| "parameters": [ | ||
| { | ||
| "name": "options", | ||
| "docs": "", | ||
| "type": "IsDataSavedOptions" | ||
| } | ||
| ], | ||
| "returns": "Promise<IsDataSavedResult>", | ||
| "tags": [ | ||
| { | ||
| "name": "param", | ||
| "text": "options" | ||
| }, | ||
| { | ||
| "name": "returns" | ||
| }, | ||
| { | ||
| "name": "memberof", | ||
| "text": "NativeBiometricPlugin" | ||
| }, | ||
| { | ||
| "name": "since", | ||
| "text": "8.6.0" | ||
| } | ||
| ], | ||
| "docs": "Checks whether a value is already saved for the given key.", | ||
| "complexTypes": [ | ||
| "IsDataSavedResult", | ||
| "IsDataSavedOptions" | ||
| ], | ||
| "slug": "isdatasaved" | ||
| }, | ||
| { | ||
| "name": "getPluginVersion", | ||
@@ -329,3 +502,3 @@ "signature": "() => Promise<{ version: string; }>", | ||
| "tags": [], | ||
| "docs": "Whether authentication is available.\n\nOn Android, `verifyIdentity()` uses a `CryptoObject` backed by\n`BIOMETRIC_STRONG`, so weak-only biometric methods such as some face unlock\nimplementations are reported via `biometryType`/`authenticationStrength`\nbut do not make this value `true`.\nIf `useFallback` is true, PIN/pattern/password can make this value true.", | ||
| "docs": "Whether authentication is available.\n\nOn Android, weak-only biometrics (such as some face unlock implementations)\nare available for `verifyIdentity()` when no `allowedBiometryTypes` filter\nexcludes them. If `useFallback` is true, PIN/pattern/password can also make\nthis value true.", | ||
| "complexTypes": [], | ||
@@ -490,3 +663,3 @@ "type": "boolean" | ||
| ], | ||
| "docs": "Only for Android.\nSpecify which biometry types are allowed for authentication.\nIf not specified, all available types will be allowed.", | ||
| "docs": "Only for Android.\nSpecify which biometry types are allowed for authentication.\nIf not specified, all enrolled biometric classes (strong and weak) are allowed.\nOn Android, face unlock is often classified as weak biometrics — include\n`BiometryType.FACE_AUTHENTICATION` or omit this option to allow it.\nUse `BiometryType.DEVICE_CREDENTIAL` for PIN/pattern/password (disables the cancel button).", | ||
| "complexTypes": [ | ||
@@ -732,2 +905,222 @@ "BiometryType" | ||
| ] | ||
| }, | ||
| { | ||
| "name": "SetDataOptions", | ||
| "slug": "setdataoptions", | ||
| "docs": "", | ||
| "tags": [], | ||
| "methods": [], | ||
| "properties": [ | ||
| { | ||
| "name": "key", | ||
| "tags": [], | ||
| "docs": "Unique identifier for the stored value.\nUse a stable app-specific namespace (e.g. `pin`, `session.token`).", | ||
| "complexTypes": [], | ||
| "type": "string" | ||
| }, | ||
| { | ||
| "name": "value", | ||
| "tags": [], | ||
| "docs": "Arbitrary string payload. Serialize objects with `JSON.stringify()` before storing.\n\nPlatform limits apply: Android Keystore-backed encryption works best with payloads\nunder ~8 KB; iOS Keychain practical limits are higher but very large values are discouraged.", | ||
| "complexTypes": [], | ||
| "type": "string" | ||
| }, | ||
| { | ||
| "name": "accessControl", | ||
| "tags": [ | ||
| { | ||
| "text": "AccessControl.NONE", | ||
| "name": "default" | ||
| }, | ||
| { | ||
| "text": "8.6.0", | ||
| "name": "since" | ||
| } | ||
| ], | ||
| "docs": "Access control level for the stored value.\nWhen set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the value is hardware-protected\nand requires biometric authentication to access via `getSecureData()`.", | ||
| "complexTypes": [ | ||
| "AccessControl" | ||
| ], | ||
| "type": "AccessControl" | ||
| }, | ||
| { | ||
| "name": "authValidityDuration", | ||
| "tags": [ | ||
| { | ||
| "text": "0", | ||
| "name": "default" | ||
| }, | ||
| { | ||
| "text": "8.6.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.", | ||
| "complexTypes": [], | ||
| "type": "number | undefined" | ||
| }, | ||
| { | ||
| "name": "title", | ||
| "tags": [ | ||
| { | ||
| "text": "\"Protect Data\"", | ||
| "name": "default" | ||
| }, | ||
| { | ||
| "text": "8.6.0", | ||
| "name": "since" | ||
| } | ||
| ], | ||
| "docs": "Title for the biometric prompt shown while protecting data.\nOnly for Android.", | ||
| "complexTypes": [], | ||
| "type": "string | undefined" | ||
| }, | ||
| { | ||
| "name": "negativeButtonText", | ||
| "tags": [ | ||
| { | ||
| "text": "\"Cancel\"", | ||
| "name": "default" | ||
| }, | ||
| { | ||
| "text": "8.6.0", | ||
| "name": "since" | ||
| } | ||
| ], | ||
| "docs": "Text for the negative/cancel button in the biometric prompt.\nOnly for Android.", | ||
| "complexTypes": [], | ||
| "type": "string | undefined" | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "StoredData", | ||
| "slug": "storeddata", | ||
| "docs": "", | ||
| "tags": [], | ||
| "methods": [], | ||
| "properties": [ | ||
| { | ||
| "name": "value", | ||
| "tags": [], | ||
| "docs": "", | ||
| "complexTypes": [], | ||
| "type": "string" | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "GetDataOptions", | ||
| "slug": "getdataoptions", | ||
| "docs": "", | ||
| "tags": [], | ||
| "methods": [], | ||
| "properties": [ | ||
| { | ||
| "name": "key", | ||
| "tags": [], | ||
| "docs": "", | ||
| "complexTypes": [], | ||
| "type": "string" | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "GetSecureDataOptions", | ||
| "slug": "getsecuredataoptions", | ||
| "docs": "", | ||
| "tags": [], | ||
| "methods": [], | ||
| "properties": [ | ||
| { | ||
| "name": "key", | ||
| "tags": [], | ||
| "docs": "", | ||
| "complexTypes": [], | ||
| "type": "string" | ||
| }, | ||
| { | ||
| "name": "reason", | ||
| "tags": [], | ||
| "docs": "Reason for requesting biometric authentication.\nDisplayed in the biometric prompt on both iOS and Android.", | ||
| "complexTypes": [], | ||
| "type": "string | undefined" | ||
| }, | ||
| { | ||
| "name": "title", | ||
| "tags": [], | ||
| "docs": "Title for the biometric prompt.\nOnly for Android.", | ||
| "complexTypes": [], | ||
| "type": "string | undefined" | ||
| }, | ||
| { | ||
| "name": "subtitle", | ||
| "tags": [], | ||
| "docs": "Subtitle for the biometric prompt.\nOnly for Android.", | ||
| "complexTypes": [], | ||
| "type": "string | undefined" | ||
| }, | ||
| { | ||
| "name": "description", | ||
| "tags": [], | ||
| "docs": "Description for the biometric prompt.\nOnly for Android.", | ||
| "complexTypes": [], | ||
| "type": "string | undefined" | ||
| }, | ||
| { | ||
| "name": "negativeButtonText", | ||
| "tags": [], | ||
| "docs": "Text for the negative/cancel button.\nOnly for Android.", | ||
| "complexTypes": [], | ||
| "type": "string | undefined" | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "DeleteDataOptions", | ||
| "slug": "deletedataoptions", | ||
| "docs": "", | ||
| "tags": [], | ||
| "methods": [], | ||
| "properties": [ | ||
| { | ||
| "name": "key", | ||
| "tags": [], | ||
| "docs": "", | ||
| "complexTypes": [], | ||
| "type": "string" | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "IsDataSavedResult", | ||
| "slug": "isdatasavedresult", | ||
| "docs": "", | ||
| "tags": [], | ||
| "methods": [], | ||
| "properties": [ | ||
| { | ||
| "name": "isSaved", | ||
| "tags": [], | ||
| "docs": "", | ||
| "complexTypes": [], | ||
| "type": "boolean" | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "IsDataSavedOptions", | ||
| "slug": "isdatasavedoptions", | ||
| "docs": "", | ||
| "tags": [], | ||
| "methods": [], | ||
| "properties": [ | ||
| { | ||
| "name": "key", | ||
| "tags": [], | ||
| "docs": "", | ||
| "complexTypes": [], | ||
| "type": "string" | ||
| } | ||
| ] | ||
| } | ||
@@ -734,0 +1127,0 @@ ], |
@@ -73,7 +73,6 @@ import type { PluginListenerHandle } from '@capacitor/core'; | ||
| * | ||
| * On Android, `verifyIdentity()` uses a `CryptoObject` backed by | ||
| * `BIOMETRIC_STRONG`, so weak-only biometric methods such as some face unlock | ||
| * implementations are reported via `biometryType`/`authenticationStrength` | ||
| * but do not make this value `true`. | ||
| * If `useFallback` is true, PIN/pattern/password can make this value true. | ||
| * On Android, weak-only biometrics (such as some face unlock implementations) | ||
| * are available for `verifyIdentity()` when no `allowedBiometryTypes` filter | ||
| * excludes them. If `useFallback` is true, PIN/pattern/password can also make | ||
| * this value true. | ||
| */ | ||
@@ -136,3 +135,6 @@ isAvailable: boolean; | ||
| * Specify which biometry types are allowed for authentication. | ||
| * If not specified, all available types will be allowed. | ||
| * If not specified, all enrolled biometric classes (strong and weak) are allowed. | ||
| * On Android, face unlock is often classified as weak biometrics — include | ||
| * `BiometryType.FACE_AUTHENTICATION` or omit this option to allow it. | ||
| * Use `BiometryType.DEVICE_CREDENTIAL` for PIN/pattern/password (disables the cancel button). | ||
| * @example [BiometryType.FINGERPRINT, BiometryType.FACE_AUTHENTICATION] | ||
@@ -238,2 +240,92 @@ */ | ||
| } | ||
| export interface SetDataOptions { | ||
| /** | ||
| * Unique identifier for the stored value. | ||
| * Use a stable app-specific namespace (e.g. `pin`, `session.token`). | ||
| */ | ||
| key: string; | ||
| /** | ||
| * Arbitrary string payload. Serialize objects with `JSON.stringify()` before storing. | ||
| * | ||
| * Platform limits apply: Android Keystore-backed encryption works best with payloads | ||
| * under ~8 KB; iOS Keychain practical limits are higher but very large values are discouraged. | ||
| */ | ||
| value: string; | ||
| /** | ||
| * Access control level for the stored value. | ||
| * When set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the value is hardware-protected | ||
| * and requires biometric authentication to access via `getSecureData()`. | ||
| * | ||
| * @default AccessControl.NONE | ||
| * @since 8.6.0 | ||
| */ | ||
| accessControl?: AccessControl; | ||
| /** | ||
| * Only for Android. Ignored on iOS and web. | ||
| * Only meaningful together with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY. | ||
| * | ||
| * @default 0 | ||
| * @since 8.6.0 | ||
| */ | ||
| authValidityDuration?: number; | ||
| /** | ||
| * Title for the biometric prompt shown while protecting data. | ||
| * Only for Android. | ||
| * | ||
| * @default "Protect Data" | ||
| * @since 8.6.0 | ||
| */ | ||
| title?: string; | ||
| /** | ||
| * Text for the negative/cancel button in the biometric prompt. | ||
| * Only for Android. | ||
| * | ||
| * @default "Cancel" | ||
| * @since 8.6.0 | ||
| */ | ||
| negativeButtonText?: string; | ||
| } | ||
| export interface GetDataOptions { | ||
| key: string; | ||
| } | ||
| export interface GetSecureDataOptions { | ||
| key: string; | ||
| /** | ||
| * Reason for requesting biometric authentication. | ||
| * Displayed in the biometric prompt on both iOS and Android. | ||
| */ | ||
| reason?: string; | ||
| /** | ||
| * Title for the biometric prompt. | ||
| * Only for Android. | ||
| */ | ||
| title?: string; | ||
| /** | ||
| * Subtitle for the biometric prompt. | ||
| * Only for Android. | ||
| */ | ||
| subtitle?: string; | ||
| /** | ||
| * Description for the biometric prompt. | ||
| * Only for Android. | ||
| */ | ||
| description?: string; | ||
| /** | ||
| * Text for the negative/cancel button. | ||
| * Only for Android. | ||
| */ | ||
| negativeButtonText?: string; | ||
| } | ||
| export interface StoredData { | ||
| value: string; | ||
| } | ||
| export interface DeleteDataOptions { | ||
| key: string; | ||
| } | ||
| export interface IsDataSavedOptions { | ||
| key: string; | ||
| } | ||
| export interface IsDataSavedResult { | ||
| isSaved: boolean; | ||
| } | ||
| /** | ||
@@ -400,2 +492,54 @@ * Biometric authentication error codes. | ||
| /** | ||
| * Stores an arbitrary string value under the given key. | ||
| * Values are encrypted at rest using the platform secure storage backend | ||
| * (Android Keystore + SharedPreferences, iOS Keychain). | ||
| * | ||
| * For biometric-protected storage, set `accessControl` and retrieve the value | ||
| * with `getSecureData()`. Credential helpers remain available for username/password flows. | ||
| * | ||
| * @param {SetDataOptions} options | ||
| * @returns {Promise<void>} | ||
| * @memberof NativeBiometricPlugin | ||
| * @since 8.6.0 | ||
| */ | ||
| setData(options: SetDataOptions): Promise<void>; | ||
| /** | ||
| * Gets a previously stored value for the given key. | ||
| * Only returns values stored without biometric `accessControl`. | ||
| * | ||
| * @param {GetDataOptions} options | ||
| * @returns {Promise<StoredData>} | ||
| * @memberof NativeBiometricPlugin | ||
| * @since 8.6.0 | ||
| */ | ||
| getData(options: GetDataOptions): Promise<StoredData>; | ||
| /** | ||
| * Gets a biometric-protected value for the given key. | ||
| * The value must have been stored with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY. | ||
| * | ||
| * @param {GetSecureDataOptions} options | ||
| * @returns {Promise<StoredData>} | ||
| * @memberof NativeBiometricPlugin | ||
| * @since 8.6.0 | ||
| */ | ||
| getSecureData(options: GetSecureDataOptions): Promise<StoredData>; | ||
| /** | ||
| * Deletes the stored value for the given key (protected and unprotected). | ||
| * | ||
| * @param {DeleteDataOptions} options | ||
| * @returns {Promise<void>} | ||
| * @memberof NativeBiometricPlugin | ||
| * @since 8.6.0 | ||
| */ | ||
| deleteData(options: DeleteDataOptions): Promise<void>; | ||
| /** | ||
| * Checks whether a value is already saved for the given key. | ||
| * | ||
| * @param {IsDataSavedOptions} options | ||
| * @returns {Promise<IsDataSavedResult>} | ||
| * @memberof NativeBiometricPlugin | ||
| * @since 8.6.0 | ||
| */ | ||
| isDataSaved(options: IsDataSavedOptions): Promise<IsDataSavedResult>; | ||
| /** | ||
| * Get the native Capacitor plugin version. | ||
@@ -402,0 +546,0 @@ * |
@@ -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;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"]} | ||
| {"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;AA2SD;;;;;;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, weak-only biometrics (such as some face unlock implementations)\n * are available for `verifyIdentity()` when no `allowedBiometryTypes` filter\n * excludes them. If `useFallback` is true, PIN/pattern/password can also make\n * 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 enrolled biometric classes (strong and weak) are allowed.\n * On Android, face unlock is often classified as weak biometrics — include\n * `BiometryType.FACE_AUTHENTICATION` or omit this option to allow it.\n * Use `BiometryType.DEVICE_CREDENTIAL` for PIN/pattern/password (disables the cancel button).\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}\nexport interface SetDataOptions {\n /**\n * Unique identifier for the stored value.\n * Use a stable app-specific namespace (e.g. `pin`, `session.token`).\n */\n key: string;\n /**\n * Arbitrary string payload. Serialize objects with `JSON.stringify()` before storing.\n *\n * Platform limits apply: Android Keystore-backed encryption works best with payloads\n * under ~8 KB; iOS Keychain practical limits are higher but very large values are discouraged.\n */\n value: string;\n /**\n * Access control level for the stored value.\n * When set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the value is hardware-protected\n * and requires biometric authentication to access via `getSecureData()`.\n *\n * @default AccessControl.NONE\n * @since 8.6.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 * @default 0\n * @since 8.6.0\n */\n authValidityDuration?: number;\n /**\n * Title for the biometric prompt shown while protecting data.\n * Only for Android.\n *\n * @default \"Protect Data\"\n * @since 8.6.0\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.6.0\n */\n negativeButtonText?: string;\n}\n\nexport interface GetDataOptions {\n key: string;\n}\n\nexport interface GetSecureDataOptions {\n key: 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 StoredData {\n value: string;\n}\n\nexport interface DeleteDataOptions {\n key: string;\n}\n\nexport interface IsDataSavedOptions {\n key: string;\n}\n\nexport interface IsDataSavedResult {\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 * Stores an arbitrary string value under the given key.\n * Values are encrypted at rest using the platform secure storage backend\n * (Android Keystore + SharedPreferences, iOS Keychain).\n *\n * For biometric-protected storage, set `accessControl` and retrieve the value\n * with `getSecureData()`. Credential helpers remain available for username/password flows.\n *\n * @param {SetDataOptions} options\n * @returns {Promise<void>}\n * @memberof NativeBiometricPlugin\n * @since 8.6.0\n */\n setData(options: SetDataOptions): Promise<void>;\n\n /**\n * Gets a previously stored value for the given key.\n * Only returns values stored without biometric `accessControl`.\n *\n * @param {GetDataOptions} options\n * @returns {Promise<StoredData>}\n * @memberof NativeBiometricPlugin\n * @since 8.6.0\n */\n getData(options: GetDataOptions): Promise<StoredData>;\n\n /**\n * Gets a biometric-protected value for the given key.\n * The value must have been stored with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY.\n *\n * @param {GetSecureDataOptions} options\n * @returns {Promise<StoredData>}\n * @memberof NativeBiometricPlugin\n * @since 8.6.0\n */\n getSecureData(options: GetSecureDataOptions): Promise<StoredData>;\n\n /**\n * Deletes the stored value for the given key (protected and unprotected).\n *\n * @param {DeleteDataOptions} options\n * @returns {Promise<void>}\n * @memberof NativeBiometricPlugin\n * @since 8.6.0\n */\n deleteData(options: DeleteDataOptions): Promise<void>;\n\n /**\n * Checks whether a value is already saved for the given key.\n *\n * @param {IsDataSavedOptions} options\n * @returns {Promise<IsDataSavedResult>}\n * @memberof NativeBiometricPlugin\n * @since 8.6.0\n */\n isDataSaved(options: IsDataSavedOptions): Promise<IsDataSavedResult>;\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"]} |
| import { WebPlugin } from '@capacitor/core'; | ||
| import type { PluginListenerHandle } from '@capacitor/core'; | ||
| import type { NativeBiometricPlugin, AvailableResult, BiometricOptions, GetCredentialOptions, GetSecureCredentialsOptions, SetCredentialOptions, DeleteCredentialOptions, IsCredentialsSavedOptions, IsCredentialsSavedResult, Credentials, BiometryChangeListener } from './definitions'; | ||
| import type { NativeBiometricPlugin, AvailableResult, BiometricOptions, GetCredentialOptions, GetSecureCredentialsOptions, SetCredentialOptions, DeleteCredentialOptions, IsCredentialsSavedOptions, IsCredentialsSavedResult, SetDataOptions, GetDataOptions, GetSecureDataOptions, DeleteDataOptions, IsDataSavedOptions, IsDataSavedResult, StoredData, Credentials, BiometryChangeListener } from './definitions'; | ||
| export declare class NativeBiometricWeb extends WebPlugin implements NativeBiometricPlugin { | ||
@@ -11,2 +11,3 @@ /** | ||
| private credentialStore; | ||
| private dataStore; | ||
| constructor(); | ||
@@ -21,2 +22,7 @@ isAvailable(): Promise<AvailableResult>; | ||
| isCredentialsSaved(_options: IsCredentialsSavedOptions): Promise<IsCredentialsSavedResult>; | ||
| setData(_options: SetDataOptions): Promise<void>; | ||
| getData(_options: GetDataOptions): Promise<StoredData>; | ||
| getSecureData(_options: GetSecureDataOptions): Promise<StoredData>; | ||
| deleteData(_options: DeleteDataOptions): Promise<void>; | ||
| isDataSaved(_options: IsDataSavedOptions): Promise<IsDataSavedResult>; | ||
| getPluginVersion(): Promise<{ | ||
@@ -23,0 +29,0 @@ version: string; |
+31
-0
@@ -12,2 +12,3 @@ import { WebPlugin } from '@capacitor/core'; | ||
| this.credentialStore = new Map(); | ||
| this.dataStore = new Map(); | ||
| } | ||
@@ -76,2 +77,32 @@ isAvailable() { | ||
| } | ||
| setData(_options) { | ||
| console.log('setData (dummy implementation)', { key: _options.key }); | ||
| this.dataStore.set(_options.key, _options.value); | ||
| return Promise.resolve(); | ||
| } | ||
| getData(_options) { | ||
| console.log('getData (dummy implementation)', { key: _options.key }); | ||
| const value = this.dataStore.get(_options.key); | ||
| if (value === undefined) { | ||
| throw new Error('No data found for the specified key'); | ||
| } | ||
| return Promise.resolve({ value }); | ||
| } | ||
| getSecureData(_options) { | ||
| console.log('getSecureData (dummy implementation)', { key: _options.key }); | ||
| const value = this.dataStore.get(_options.key); | ||
| if (value === undefined) { | ||
| throw new Error('No protected data found for the specified key'); | ||
| } | ||
| return Promise.resolve({ value }); | ||
| } | ||
| deleteData(_options) { | ||
| console.log('deleteData (dummy implementation)', { key: _options.key }); | ||
| this.dataStore.delete(_options.key); | ||
| return Promise.resolve(); | ||
| } | ||
| isDataSaved(_options) { | ||
| console.log('isDataSaved (dummy implementation)', { key: _options.key }); | ||
| return Promise.resolve({ isSaved: this.dataStore.has(_options.key) }); | ||
| } | ||
| async getPluginVersion() { | ||
@@ -78,0 +109,0 @@ return { version: 'web' }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAgB5C,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAErE,MAAM,OAAO,kBAAmB,SAAQ,SAAS;IAQ/C;QACE,KAAK,EAAE,CAAC;QARV;;;;WAIG;QACK,oBAAe,GAA6B,IAAI,GAAG,EAAE,CAAC;IAI9D,CAAC;IAED,WAAW;QACT,sEAAsE;QACtE,iFAAiF;QACjF,OAAO,OAAO,CAAC,OAAO,CAAC;YACrB,WAAW,EAAE,IAAI;YACjB,sBAAsB,EAAE,sBAAsB,CAAC,MAAM;YACrD,YAAY,EAAE,YAAY,CAAC,QAAQ;YACnC,cAAc,EAAE,IAAI;YACpB,yBAAyB,EAAE,IAAI;SAChC,CAAC,CAAC;IACL,CAAC;IAED,6DAA6D;IAC7D,KAAK,CAAC,WAAW,CAAC,UAA4B,EAAE,SAAiC;QAC/E,iDAAiD;QACjD,OAAO;YACL,MAAM,EAAE,KAAK,IAAI,EAAE;gBACjB,2BAA2B;YAC7B,CAAC;SACF,CAAC;IACJ,CAAC;IAED,6DAA6D;IAC7D,cAAc,CAAC,QAA2B;QACxC,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QACrD,4DAA4D;QAC5D,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,cAAc,CAAC,QAA8B;QAC3C,OAAO,CAAC,GAAG,CAAC,uCAAuC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAClF,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC9D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC;IAED,oBAAoB,CAAC,QAAqC;QACxD,OAAO,CAAC,GAAG,CAAC,6CAA6C,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACxF,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC9D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC;IAED,cAAc,CAAC,QAA8B;QAC3C,OAAO,CAAC,GAAG,CAAC,uCAAuC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAClF,wCAAwC;QACxC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE;YACxC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;SAC5B,CAAC,CAAC;QACH,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,iBAAiB,CAAC,QAAiC;QACjD,OAAO,CAAC,GAAG,CAAC,0CAA0C,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACrF,oDAAoD;QACpD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC7C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,kBAAkB,CAAC,QAAmC;QACpD,OAAO,CAAC,GAAG,CAAC,2CAA2C,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACtF,8CAA8C;QAC9C,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { PluginListenerHandle } from '@capacitor/core';\n\nimport type {\n NativeBiometricPlugin,\n AvailableResult,\n BiometricOptions,\n GetCredentialOptions,\n GetSecureCredentialsOptions,\n SetCredentialOptions,\n DeleteCredentialOptions,\n IsCredentialsSavedOptions,\n IsCredentialsSavedResult,\n Credentials,\n BiometryChangeListener,\n} from './definitions';\nimport { BiometryType, AuthenticationStrength } from './definitions';\n\nexport class NativeBiometricWeb extends WebPlugin implements NativeBiometricPlugin {\n /**\n * In-memory credential storage for browser development/testing.\n * Credentials are stored temporarily and cleared on page refresh.\n * This is NOT secure storage and should only be used for development purposes.\n */\n private credentialStore: Map<string, Credentials> = new Map();\n\n constructor() {\n super();\n }\n\n isAvailable(): Promise<AvailableResult> {\n // Web platform: return a dummy implementation for development/testing\n // Using TOUCH_ID as a generic placeholder for simulated biometric authentication\n return Promise.resolve({\n isAvailable: true,\n authenticationStrength: AuthenticationStrength.STRONG,\n biometryType: BiometryType.TOUCH_ID,\n deviceIsSecure: true,\n strongBiometryIsAvailable: true,\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async addListener(_eventName: 'biometryChange', _listener: BiometryChangeListener): Promise<PluginListenerHandle> {\n // Web platform: no-op, but return a valid handle\n return {\n remove: async () => {\n // Nothing to remove on web\n },\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n verifyIdentity(_options?: BiometricOptions): Promise<void> {\n console.log('verifyIdentity (dummy implementation)');\n // Dummy implementation: always succeeds for browser testing\n return Promise.resolve();\n }\n\n getCredentials(_options: GetCredentialOptions): Promise<Credentials> {\n console.log('getCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n\n getSecureCredentials(_options: GetSecureCredentialsOptions): Promise<Credentials> {\n console.log('getSecureCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n\n setCredentials(_options: SetCredentialOptions): Promise<void> {\n console.log('setCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: store in memory\n this.credentialStore.set(_options.server, {\n username: _options.username,\n password: _options.password,\n });\n return Promise.resolve();\n }\n\n deleteCredentials(_options: DeleteCredentialOptions): Promise<void> {\n console.log('deleteCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: remove from in-memory store\n this.credentialStore.delete(_options.server);\n return Promise.resolve();\n }\n\n isCredentialsSaved(_options: IsCredentialsSavedOptions): Promise<IsCredentialsSavedResult> {\n console.log('isCredentialsSaved (dummy implementation)', { server: _options.server });\n // Dummy implementation: check in-memory store\n return Promise.resolve({ isSaved: this.credentialStore.has(_options.server) });\n }\n\n async getPluginVersion(): Promise<{ version: string }> {\n return { version: 'web' };\n }\n}\n"]} | ||
| {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { PluginListenerHandle } from '@capacitor/core';\n\nimport type {\n NativeBiometricPlugin,\n AvailableResult,\n BiometricOptions,\n GetCredentialOptions,\n GetSecureCredentialsOptions,\n SetCredentialOptions,\n DeleteCredentialOptions,\n IsCredentialsSavedOptions,\n IsCredentialsSavedResult,\n SetDataOptions,\n GetDataOptions,\n GetSecureDataOptions,\n DeleteDataOptions,\n IsDataSavedOptions,\n IsDataSavedResult,\n StoredData,\n Credentials,\n BiometryChangeListener,\n} from './definitions';\nimport { BiometryType, AuthenticationStrength } from './definitions';\n\nexport class NativeBiometricWeb extends WebPlugin implements NativeBiometricPlugin {\n /**\n * In-memory credential storage for browser development/testing.\n * Credentials are stored temporarily and cleared on page refresh.\n * This is NOT secure storage and should only be used for development purposes.\n */\n private credentialStore: Map<string, Credentials> = new Map();\n private dataStore: Map<string, string> = new Map();\n\n constructor() {\n super();\n }\n\n isAvailable(): Promise<AvailableResult> {\n // Web platform: return a dummy implementation for development/testing\n // Using TOUCH_ID as a generic placeholder for simulated biometric authentication\n return Promise.resolve({\n isAvailable: true,\n authenticationStrength: AuthenticationStrength.STRONG,\n biometryType: BiometryType.TOUCH_ID,\n deviceIsSecure: true,\n strongBiometryIsAvailable: true,\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async addListener(_eventName: 'biometryChange', _listener: BiometryChangeListener): Promise<PluginListenerHandle> {\n // Web platform: no-op, but return a valid handle\n return {\n remove: async () => {\n // Nothing to remove on web\n },\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n verifyIdentity(_options?: BiometricOptions): Promise<void> {\n console.log('verifyIdentity (dummy implementation)');\n // Dummy implementation: always succeeds for browser testing\n return Promise.resolve();\n }\n\n getCredentials(_options: GetCredentialOptions): Promise<Credentials> {\n console.log('getCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n\n getSecureCredentials(_options: GetSecureCredentialsOptions): Promise<Credentials> {\n console.log('getSecureCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n\n setCredentials(_options: SetCredentialOptions): Promise<void> {\n console.log('setCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: store in memory\n this.credentialStore.set(_options.server, {\n username: _options.username,\n password: _options.password,\n });\n return Promise.resolve();\n }\n\n deleteCredentials(_options: DeleteCredentialOptions): Promise<void> {\n console.log('deleteCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: remove from in-memory store\n this.credentialStore.delete(_options.server);\n return Promise.resolve();\n }\n\n isCredentialsSaved(_options: IsCredentialsSavedOptions): Promise<IsCredentialsSavedResult> {\n console.log('isCredentialsSaved (dummy implementation)', { server: _options.server });\n // Dummy implementation: check in-memory store\n return Promise.resolve({ isSaved: this.credentialStore.has(_options.server) });\n }\n\n setData(_options: SetDataOptions): Promise<void> {\n console.log('setData (dummy implementation)', { key: _options.key });\n this.dataStore.set(_options.key, _options.value);\n return Promise.resolve();\n }\n\n getData(_options: GetDataOptions): Promise<StoredData> {\n console.log('getData (dummy implementation)', { key: _options.key });\n const value = this.dataStore.get(_options.key);\n if (value === undefined) {\n throw new Error('No data found for the specified key');\n }\n return Promise.resolve({ value });\n }\n\n getSecureData(_options: GetSecureDataOptions): Promise<StoredData> {\n console.log('getSecureData (dummy implementation)', { key: _options.key });\n const value = this.dataStore.get(_options.key);\n if (value === undefined) {\n throw new Error('No protected data found for the specified key');\n }\n return Promise.resolve({ value });\n }\n\n deleteData(_options: DeleteDataOptions): Promise<void> {\n console.log('deleteData (dummy implementation)', { key: _options.key });\n this.dataStore.delete(_options.key);\n return Promise.resolve();\n }\n\n isDataSaved(_options: IsDataSavedOptions): Promise<IsDataSavedResult> {\n console.log('isDataSaved (dummy implementation)', { key: _options.key });\n return Promise.resolve({ isSaved: this.dataStore.has(_options.key) });\n }\n\n async getPluginVersion(): Promise<{ version: string }> {\n return { version: 'web' };\n }\n}\n"]} |
+31
-0
@@ -149,2 +149,3 @@ 'use strict'; | ||
| this.credentialStore = new Map(); | ||
| this.dataStore = new Map(); | ||
| } | ||
@@ -213,2 +214,32 @@ isAvailable() { | ||
| } | ||
| setData(_options) { | ||
| console.log('setData (dummy implementation)', { key: _options.key }); | ||
| this.dataStore.set(_options.key, _options.value); | ||
| return Promise.resolve(); | ||
| } | ||
| getData(_options) { | ||
| console.log('getData (dummy implementation)', { key: _options.key }); | ||
| const value = this.dataStore.get(_options.key); | ||
| if (value === undefined) { | ||
| throw new Error('No data found for the specified key'); | ||
| } | ||
| return Promise.resolve({ value }); | ||
| } | ||
| getSecureData(_options) { | ||
| console.log('getSecureData (dummy implementation)', { key: _options.key }); | ||
| const value = this.dataStore.get(_options.key); | ||
| if (value === undefined) { | ||
| throw new Error('No protected data found for the specified key'); | ||
| } | ||
| return Promise.resolve({ value }); | ||
| } | ||
| deleteData(_options) { | ||
| console.log('deleteData (dummy implementation)', { key: _options.key }); | ||
| this.dataStore.delete(_options.key); | ||
| return Promise.resolve(); | ||
| } | ||
| isDataSaved(_options) { | ||
| console.log('isDataSaved (dummy implementation)', { key: _options.key }); | ||
| return Promise.resolve({ isSaved: this.dataStore.has(_options.key) }); | ||
| } | ||
| async getPluginVersion() { | ||
@@ -215,0 +246,0 @@ return { version: 'web' }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["export var BiometryType;\n(function (BiometryType) {\n // Android, iOS\n BiometryType[BiometryType[\"NONE\"] = 0] = \"NONE\";\n // iOS\n BiometryType[BiometryType[\"TOUCH_ID\"] = 1] = \"TOUCH_ID\";\n // iOS\n BiometryType[BiometryType[\"FACE_ID\"] = 2] = \"FACE_ID\";\n // Android\n BiometryType[BiometryType[\"FINGERPRINT\"] = 3] = \"FINGERPRINT\";\n // Android\n BiometryType[BiometryType[\"FACE_AUTHENTICATION\"] = 4] = \"FACE_AUTHENTICATION\";\n // Android\n BiometryType[BiometryType[\"IRIS_AUTHENTICATION\"] = 5] = \"IRIS_AUTHENTICATION\";\n // Android\n BiometryType[BiometryType[\"MULTIPLE\"] = 6] = \"MULTIPLE\";\n // Android - Device credentials (PIN, pattern, or password)\n BiometryType[BiometryType[\"DEVICE_CREDENTIAL\"] = 7] = \"DEVICE_CREDENTIAL\";\n})(BiometryType || (BiometryType = {}));\nexport var AuthenticationStrength;\n(function (AuthenticationStrength) {\n /**\n * No authentication available, even if PIN is available but useFallback = false\n */\n AuthenticationStrength[AuthenticationStrength[\"NONE\"] = 0] = \"NONE\";\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 AuthenticationStrength[AuthenticationStrength[\"STRONG\"] = 1] = \"STRONG\";\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 AuthenticationStrength[AuthenticationStrength[\"WEAK\"] = 2] = \"WEAK\";\n})(AuthenticationStrength || (AuthenticationStrength = {}));\nexport var AccessControl;\n(function (AccessControl) {\n /**\n * No biometric protection. Credentials are accessible without authentication.\n * This is the default behavior for backward compatibility.\n */\n AccessControl[AccessControl[\"NONE\"] = 0] = \"NONE\";\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 AccessControl[AccessControl[\"BIOMETRY_CURRENT_SET\"] = 1] = \"BIOMETRY_CURRENT_SET\";\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 AccessControl[AccessControl[\"BIOMETRY_ANY\"] = 2] = \"BIOMETRY_ANY\";\n})(AccessControl || (AccessControl = {}));\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 var BiometricAuthError;\n(function (BiometricAuthError) {\n /**\n * Unknown error occurred\n */\n BiometricAuthError[BiometricAuthError[\"UNKNOWN_ERROR\"] = 0] = \"UNKNOWN_ERROR\";\n /**\n * Biometrics are unavailable (no hardware or hardware error)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"BIOMETRICS_UNAVAILABLE\"] = 1] = \"BIOMETRICS_UNAVAILABLE\";\n /**\n * User has been locked out due to too many failed attempts\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_LOCKOUT\"] = 2] = \"USER_LOCKOUT\";\n /**\n * No biometrics are enrolled on the device\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"BIOMETRICS_NOT_ENROLLED\"] = 3] = \"BIOMETRICS_NOT_ENROLLED\";\n /**\n * User is temporarily locked out (Android: 30 second lockout)\n * Platform: Android\n */\n BiometricAuthError[BiometricAuthError[\"USER_TEMPORARY_LOCKOUT\"] = 4] = \"USER_TEMPORARY_LOCKOUT\";\n /**\n * Authentication failed (user did not authenticate successfully)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"AUTHENTICATION_FAILED\"] = 10] = \"AUTHENTICATION_FAILED\";\n /**\n * App canceled the authentication (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"APP_CANCEL\"] = 11] = \"APP_CANCEL\";\n /**\n * Invalid context (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"INVALID_CONTEXT\"] = 12] = \"INVALID_CONTEXT\";\n /**\n * Authentication was not interactive (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"NOT_INTERACTIVE\"] = 13] = \"NOT_INTERACTIVE\";\n /**\n * Passcode/PIN is not set on the device\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"PASSCODE_NOT_SET\"] = 14] = \"PASSCODE_NOT_SET\";\n /**\n * System canceled the authentication (e.g., due to screen lock)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"SYSTEM_CANCEL\"] = 15] = \"SYSTEM_CANCEL\";\n /**\n * User canceled the authentication\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_CANCEL\"] = 16] = \"USER_CANCEL\";\n /**\n * User chose to use fallback authentication method\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_FALLBACK\"] = 17] = \"USER_FALLBACK\";\n})(BiometricAuthError || (BiometricAuthError = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeBiometric = registerPlugin('NativeBiometric', {\n web: () => import('./web').then((m) => new m.NativeBiometricWeb()),\n});\nexport * from './definitions';\nexport { NativeBiometric };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { BiometryType, AuthenticationStrength } from './definitions';\nexport class NativeBiometricWeb extends WebPlugin {\n constructor() {\n super();\n /**\n * In-memory credential storage for browser development/testing.\n * Credentials are stored temporarily and cleared on page refresh.\n * This is NOT secure storage and should only be used for development purposes.\n */\n this.credentialStore = new Map();\n }\n isAvailable() {\n // Web platform: return a dummy implementation for development/testing\n // Using TOUCH_ID as a generic placeholder for simulated biometric authentication\n return Promise.resolve({\n isAvailable: true,\n authenticationStrength: AuthenticationStrength.STRONG,\n biometryType: BiometryType.TOUCH_ID,\n deviceIsSecure: true,\n strongBiometryIsAvailable: true,\n });\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async addListener(_eventName, _listener) {\n // Web platform: no-op, but return a valid handle\n return {\n remove: async () => {\n // Nothing to remove on web\n },\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n verifyIdentity(_options) {\n console.log('verifyIdentity (dummy implementation)');\n // Dummy implementation: always succeeds for browser testing\n return Promise.resolve();\n }\n getCredentials(_options) {\n console.log('getCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n getSecureCredentials(_options) {\n console.log('getSecureCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n setCredentials(_options) {\n console.log('setCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: store in memory\n this.credentialStore.set(_options.server, {\n username: _options.username,\n password: _options.password,\n });\n return Promise.resolve();\n }\n deleteCredentials(_options) {\n console.log('deleteCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: remove from in-memory store\n this.credentialStore.delete(_options.server);\n return Promise.resolve();\n }\n isCredentialsSaved(_options) {\n console.log('isCredentialsSaved (dummy implementation)', { server: _options.server });\n // Dummy implementation: check in-memory store\n return Promise.resolve({ isSaved: this.credentialStore.has(_options.server) });\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BiometryType","AuthenticationStrength","AccessControl","BiometricAuthError","registerPlugin","WebPlugin"],"mappings":"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"} | ||
| {"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["export var BiometryType;\n(function (BiometryType) {\n // Android, iOS\n BiometryType[BiometryType[\"NONE\"] = 0] = \"NONE\";\n // iOS\n BiometryType[BiometryType[\"TOUCH_ID\"] = 1] = \"TOUCH_ID\";\n // iOS\n BiometryType[BiometryType[\"FACE_ID\"] = 2] = \"FACE_ID\";\n // Android\n BiometryType[BiometryType[\"FINGERPRINT\"] = 3] = \"FINGERPRINT\";\n // Android\n BiometryType[BiometryType[\"FACE_AUTHENTICATION\"] = 4] = \"FACE_AUTHENTICATION\";\n // Android\n BiometryType[BiometryType[\"IRIS_AUTHENTICATION\"] = 5] = \"IRIS_AUTHENTICATION\";\n // Android\n BiometryType[BiometryType[\"MULTIPLE\"] = 6] = \"MULTIPLE\";\n // Android - Device credentials (PIN, pattern, or password)\n BiometryType[BiometryType[\"DEVICE_CREDENTIAL\"] = 7] = \"DEVICE_CREDENTIAL\";\n})(BiometryType || (BiometryType = {}));\nexport var AuthenticationStrength;\n(function (AuthenticationStrength) {\n /**\n * No authentication available, even if PIN is available but useFallback = false\n */\n AuthenticationStrength[AuthenticationStrength[\"NONE\"] = 0] = \"NONE\";\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 AuthenticationStrength[AuthenticationStrength[\"STRONG\"] = 1] = \"STRONG\";\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 AuthenticationStrength[AuthenticationStrength[\"WEAK\"] = 2] = \"WEAK\";\n})(AuthenticationStrength || (AuthenticationStrength = {}));\nexport var AccessControl;\n(function (AccessControl) {\n /**\n * No biometric protection. Credentials are accessible without authentication.\n * This is the default behavior for backward compatibility.\n */\n AccessControl[AccessControl[\"NONE\"] = 0] = \"NONE\";\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 AccessControl[AccessControl[\"BIOMETRY_CURRENT_SET\"] = 1] = \"BIOMETRY_CURRENT_SET\";\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 AccessControl[AccessControl[\"BIOMETRY_ANY\"] = 2] = \"BIOMETRY_ANY\";\n})(AccessControl || (AccessControl = {}));\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 var BiometricAuthError;\n(function (BiometricAuthError) {\n /**\n * Unknown error occurred\n */\n BiometricAuthError[BiometricAuthError[\"UNKNOWN_ERROR\"] = 0] = \"UNKNOWN_ERROR\";\n /**\n * Biometrics are unavailable (no hardware or hardware error)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"BIOMETRICS_UNAVAILABLE\"] = 1] = \"BIOMETRICS_UNAVAILABLE\";\n /**\n * User has been locked out due to too many failed attempts\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_LOCKOUT\"] = 2] = \"USER_LOCKOUT\";\n /**\n * No biometrics are enrolled on the device\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"BIOMETRICS_NOT_ENROLLED\"] = 3] = \"BIOMETRICS_NOT_ENROLLED\";\n /**\n * User is temporarily locked out (Android: 30 second lockout)\n * Platform: Android\n */\n BiometricAuthError[BiometricAuthError[\"USER_TEMPORARY_LOCKOUT\"] = 4] = \"USER_TEMPORARY_LOCKOUT\";\n /**\n * Authentication failed (user did not authenticate successfully)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"AUTHENTICATION_FAILED\"] = 10] = \"AUTHENTICATION_FAILED\";\n /**\n * App canceled the authentication (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"APP_CANCEL\"] = 11] = \"APP_CANCEL\";\n /**\n * Invalid context (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"INVALID_CONTEXT\"] = 12] = \"INVALID_CONTEXT\";\n /**\n * Authentication was not interactive (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"NOT_INTERACTIVE\"] = 13] = \"NOT_INTERACTIVE\";\n /**\n * Passcode/PIN is not set on the device\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"PASSCODE_NOT_SET\"] = 14] = \"PASSCODE_NOT_SET\";\n /**\n * System canceled the authentication (e.g., due to screen lock)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"SYSTEM_CANCEL\"] = 15] = \"SYSTEM_CANCEL\";\n /**\n * User canceled the authentication\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_CANCEL\"] = 16] = \"USER_CANCEL\";\n /**\n * User chose to use fallback authentication method\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_FALLBACK\"] = 17] = \"USER_FALLBACK\";\n})(BiometricAuthError || (BiometricAuthError = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeBiometric = registerPlugin('NativeBiometric', {\n web: () => import('./web').then((m) => new m.NativeBiometricWeb()),\n});\nexport * from './definitions';\nexport { NativeBiometric };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { BiometryType, AuthenticationStrength } from './definitions';\nexport class NativeBiometricWeb extends WebPlugin {\n constructor() {\n super();\n /**\n * In-memory credential storage for browser development/testing.\n * Credentials are stored temporarily and cleared on page refresh.\n * This is NOT secure storage and should only be used for development purposes.\n */\n this.credentialStore = new Map();\n this.dataStore = new Map();\n }\n isAvailable() {\n // Web platform: return a dummy implementation for development/testing\n // Using TOUCH_ID as a generic placeholder for simulated biometric authentication\n return Promise.resolve({\n isAvailable: true,\n authenticationStrength: AuthenticationStrength.STRONG,\n biometryType: BiometryType.TOUCH_ID,\n deviceIsSecure: true,\n strongBiometryIsAvailable: true,\n });\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async addListener(_eventName, _listener) {\n // Web platform: no-op, but return a valid handle\n return {\n remove: async () => {\n // Nothing to remove on web\n },\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n verifyIdentity(_options) {\n console.log('verifyIdentity (dummy implementation)');\n // Dummy implementation: always succeeds for browser testing\n return Promise.resolve();\n }\n getCredentials(_options) {\n console.log('getCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n getSecureCredentials(_options) {\n console.log('getSecureCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n setCredentials(_options) {\n console.log('setCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: store in memory\n this.credentialStore.set(_options.server, {\n username: _options.username,\n password: _options.password,\n });\n return Promise.resolve();\n }\n deleteCredentials(_options) {\n console.log('deleteCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: remove from in-memory store\n this.credentialStore.delete(_options.server);\n return Promise.resolve();\n }\n isCredentialsSaved(_options) {\n console.log('isCredentialsSaved (dummy implementation)', { server: _options.server });\n // Dummy implementation: check in-memory store\n return Promise.resolve({ isSaved: this.credentialStore.has(_options.server) });\n }\n setData(_options) {\n console.log('setData (dummy implementation)', { key: _options.key });\n this.dataStore.set(_options.key, _options.value);\n return Promise.resolve();\n }\n getData(_options) {\n console.log('getData (dummy implementation)', { key: _options.key });\n const value = this.dataStore.get(_options.key);\n if (value === undefined) {\n throw new Error('No data found for the specified key');\n }\n return Promise.resolve({ value });\n }\n getSecureData(_options) {\n console.log('getSecureData (dummy implementation)', { key: _options.key });\n const value = this.dataStore.get(_options.key);\n if (value === undefined) {\n throw new Error('No protected data found for the specified key');\n }\n return Promise.resolve({ value });\n }\n deleteData(_options) {\n console.log('deleteData (dummy implementation)', { key: _options.key });\n this.dataStore.delete(_options.key);\n return Promise.resolve();\n }\n isDataSaved(_options) {\n console.log('isDataSaved (dummy implementation)', { key: _options.key });\n return Promise.resolve({ isSaved: this.dataStore.has(_options.key) });\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BiometryType","AuthenticationStrength","AccessControl","BiometricAuthError","registerPlugin","WebPlugin"],"mappings":"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"} |
+31
-0
@@ -148,2 +148,3 @@ var capacitorCapacitorBiometric = (function (exports, core) { | ||
| this.credentialStore = new Map(); | ||
| this.dataStore = new Map(); | ||
| } | ||
@@ -212,2 +213,32 @@ isAvailable() { | ||
| } | ||
| setData(_options) { | ||
| console.log('setData (dummy implementation)', { key: _options.key }); | ||
| this.dataStore.set(_options.key, _options.value); | ||
| return Promise.resolve(); | ||
| } | ||
| getData(_options) { | ||
| console.log('getData (dummy implementation)', { key: _options.key }); | ||
| const value = this.dataStore.get(_options.key); | ||
| if (value === undefined) { | ||
| throw new Error('No data found for the specified key'); | ||
| } | ||
| return Promise.resolve({ value }); | ||
| } | ||
| getSecureData(_options) { | ||
| console.log('getSecureData (dummy implementation)', { key: _options.key }); | ||
| const value = this.dataStore.get(_options.key); | ||
| if (value === undefined) { | ||
| throw new Error('No protected data found for the specified key'); | ||
| } | ||
| return Promise.resolve({ value }); | ||
| } | ||
| deleteData(_options) { | ||
| console.log('deleteData (dummy implementation)', { key: _options.key }); | ||
| this.dataStore.delete(_options.key); | ||
| return Promise.resolve(); | ||
| } | ||
| isDataSaved(_options) { | ||
| console.log('isDataSaved (dummy implementation)', { key: _options.key }); | ||
| return Promise.resolve({ isSaved: this.dataStore.has(_options.key) }); | ||
| } | ||
| async getPluginVersion() { | ||
@@ -214,0 +245,0 @@ return { version: 'web' }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["export var BiometryType;\n(function (BiometryType) {\n // Android, iOS\n BiometryType[BiometryType[\"NONE\"] = 0] = \"NONE\";\n // iOS\n BiometryType[BiometryType[\"TOUCH_ID\"] = 1] = \"TOUCH_ID\";\n // iOS\n BiometryType[BiometryType[\"FACE_ID\"] = 2] = \"FACE_ID\";\n // Android\n BiometryType[BiometryType[\"FINGERPRINT\"] = 3] = \"FINGERPRINT\";\n // Android\n BiometryType[BiometryType[\"FACE_AUTHENTICATION\"] = 4] = \"FACE_AUTHENTICATION\";\n // Android\n BiometryType[BiometryType[\"IRIS_AUTHENTICATION\"] = 5] = \"IRIS_AUTHENTICATION\";\n // Android\n BiometryType[BiometryType[\"MULTIPLE\"] = 6] = \"MULTIPLE\";\n // Android - Device credentials (PIN, pattern, or password)\n BiometryType[BiometryType[\"DEVICE_CREDENTIAL\"] = 7] = \"DEVICE_CREDENTIAL\";\n})(BiometryType || (BiometryType = {}));\nexport var AuthenticationStrength;\n(function (AuthenticationStrength) {\n /**\n * No authentication available, even if PIN is available but useFallback = false\n */\n AuthenticationStrength[AuthenticationStrength[\"NONE\"] = 0] = \"NONE\";\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 AuthenticationStrength[AuthenticationStrength[\"STRONG\"] = 1] = \"STRONG\";\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 AuthenticationStrength[AuthenticationStrength[\"WEAK\"] = 2] = \"WEAK\";\n})(AuthenticationStrength || (AuthenticationStrength = {}));\nexport var AccessControl;\n(function (AccessControl) {\n /**\n * No biometric protection. Credentials are accessible without authentication.\n * This is the default behavior for backward compatibility.\n */\n AccessControl[AccessControl[\"NONE\"] = 0] = \"NONE\";\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 AccessControl[AccessControl[\"BIOMETRY_CURRENT_SET\"] = 1] = \"BIOMETRY_CURRENT_SET\";\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 AccessControl[AccessControl[\"BIOMETRY_ANY\"] = 2] = \"BIOMETRY_ANY\";\n})(AccessControl || (AccessControl = {}));\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 var BiometricAuthError;\n(function (BiometricAuthError) {\n /**\n * Unknown error occurred\n */\n BiometricAuthError[BiometricAuthError[\"UNKNOWN_ERROR\"] = 0] = \"UNKNOWN_ERROR\";\n /**\n * Biometrics are unavailable (no hardware or hardware error)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"BIOMETRICS_UNAVAILABLE\"] = 1] = \"BIOMETRICS_UNAVAILABLE\";\n /**\n * User has been locked out due to too many failed attempts\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_LOCKOUT\"] = 2] = \"USER_LOCKOUT\";\n /**\n * No biometrics are enrolled on the device\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"BIOMETRICS_NOT_ENROLLED\"] = 3] = \"BIOMETRICS_NOT_ENROLLED\";\n /**\n * User is temporarily locked out (Android: 30 second lockout)\n * Platform: Android\n */\n BiometricAuthError[BiometricAuthError[\"USER_TEMPORARY_LOCKOUT\"] = 4] = \"USER_TEMPORARY_LOCKOUT\";\n /**\n * Authentication failed (user did not authenticate successfully)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"AUTHENTICATION_FAILED\"] = 10] = \"AUTHENTICATION_FAILED\";\n /**\n * App canceled the authentication (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"APP_CANCEL\"] = 11] = \"APP_CANCEL\";\n /**\n * Invalid context (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"INVALID_CONTEXT\"] = 12] = \"INVALID_CONTEXT\";\n /**\n * Authentication was not interactive (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"NOT_INTERACTIVE\"] = 13] = \"NOT_INTERACTIVE\";\n /**\n * Passcode/PIN is not set on the device\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"PASSCODE_NOT_SET\"] = 14] = \"PASSCODE_NOT_SET\";\n /**\n * System canceled the authentication (e.g., due to screen lock)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"SYSTEM_CANCEL\"] = 15] = \"SYSTEM_CANCEL\";\n /**\n * User canceled the authentication\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_CANCEL\"] = 16] = \"USER_CANCEL\";\n /**\n * User chose to use fallback authentication method\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_FALLBACK\"] = 17] = \"USER_FALLBACK\";\n})(BiometricAuthError || (BiometricAuthError = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeBiometric = registerPlugin('NativeBiometric', {\n web: () => import('./web').then((m) => new m.NativeBiometricWeb()),\n});\nexport * from './definitions';\nexport { NativeBiometric };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { BiometryType, AuthenticationStrength } from './definitions';\nexport class NativeBiometricWeb extends WebPlugin {\n constructor() {\n super();\n /**\n * In-memory credential storage for browser development/testing.\n * Credentials are stored temporarily and cleared on page refresh.\n * This is NOT secure storage and should only be used for development purposes.\n */\n this.credentialStore = new Map();\n }\n isAvailable() {\n // Web platform: return a dummy implementation for development/testing\n // Using TOUCH_ID as a generic placeholder for simulated biometric authentication\n return Promise.resolve({\n isAvailable: true,\n authenticationStrength: AuthenticationStrength.STRONG,\n biometryType: BiometryType.TOUCH_ID,\n deviceIsSecure: true,\n strongBiometryIsAvailable: true,\n });\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async addListener(_eventName, _listener) {\n // Web platform: no-op, but return a valid handle\n return {\n remove: async () => {\n // Nothing to remove on web\n },\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n verifyIdentity(_options) {\n console.log('verifyIdentity (dummy implementation)');\n // Dummy implementation: always succeeds for browser testing\n return Promise.resolve();\n }\n getCredentials(_options) {\n console.log('getCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n getSecureCredentials(_options) {\n console.log('getSecureCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n setCredentials(_options) {\n console.log('setCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: store in memory\n this.credentialStore.set(_options.server, {\n username: _options.username,\n password: _options.password,\n });\n return Promise.resolve();\n }\n deleteCredentials(_options) {\n console.log('deleteCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: remove from in-memory store\n this.credentialStore.delete(_options.server);\n return Promise.resolve();\n }\n isCredentialsSaved(_options) {\n console.log('isCredentialsSaved (dummy implementation)', { server: _options.server });\n // Dummy implementation: check in-memory store\n return Promise.resolve({ isSaved: this.credentialStore.has(_options.server) });\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BiometryType","AuthenticationStrength","AccessControl","BiometricAuthError","registerPlugin","WebPlugin"],"mappings":"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"} | ||
| {"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["export var BiometryType;\n(function (BiometryType) {\n // Android, iOS\n BiometryType[BiometryType[\"NONE\"] = 0] = \"NONE\";\n // iOS\n BiometryType[BiometryType[\"TOUCH_ID\"] = 1] = \"TOUCH_ID\";\n // iOS\n BiometryType[BiometryType[\"FACE_ID\"] = 2] = \"FACE_ID\";\n // Android\n BiometryType[BiometryType[\"FINGERPRINT\"] = 3] = \"FINGERPRINT\";\n // Android\n BiometryType[BiometryType[\"FACE_AUTHENTICATION\"] = 4] = \"FACE_AUTHENTICATION\";\n // Android\n BiometryType[BiometryType[\"IRIS_AUTHENTICATION\"] = 5] = \"IRIS_AUTHENTICATION\";\n // Android\n BiometryType[BiometryType[\"MULTIPLE\"] = 6] = \"MULTIPLE\";\n // Android - Device credentials (PIN, pattern, or password)\n BiometryType[BiometryType[\"DEVICE_CREDENTIAL\"] = 7] = \"DEVICE_CREDENTIAL\";\n})(BiometryType || (BiometryType = {}));\nexport var AuthenticationStrength;\n(function (AuthenticationStrength) {\n /**\n * No authentication available, even if PIN is available but useFallback = false\n */\n AuthenticationStrength[AuthenticationStrength[\"NONE\"] = 0] = \"NONE\";\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 AuthenticationStrength[AuthenticationStrength[\"STRONG\"] = 1] = \"STRONG\";\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 AuthenticationStrength[AuthenticationStrength[\"WEAK\"] = 2] = \"WEAK\";\n})(AuthenticationStrength || (AuthenticationStrength = {}));\nexport var AccessControl;\n(function (AccessControl) {\n /**\n * No biometric protection. Credentials are accessible without authentication.\n * This is the default behavior for backward compatibility.\n */\n AccessControl[AccessControl[\"NONE\"] = 0] = \"NONE\";\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 AccessControl[AccessControl[\"BIOMETRY_CURRENT_SET\"] = 1] = \"BIOMETRY_CURRENT_SET\";\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 AccessControl[AccessControl[\"BIOMETRY_ANY\"] = 2] = \"BIOMETRY_ANY\";\n})(AccessControl || (AccessControl = {}));\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 var BiometricAuthError;\n(function (BiometricAuthError) {\n /**\n * Unknown error occurred\n */\n BiometricAuthError[BiometricAuthError[\"UNKNOWN_ERROR\"] = 0] = \"UNKNOWN_ERROR\";\n /**\n * Biometrics are unavailable (no hardware or hardware error)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"BIOMETRICS_UNAVAILABLE\"] = 1] = \"BIOMETRICS_UNAVAILABLE\";\n /**\n * User has been locked out due to too many failed attempts\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_LOCKOUT\"] = 2] = \"USER_LOCKOUT\";\n /**\n * No biometrics are enrolled on the device\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"BIOMETRICS_NOT_ENROLLED\"] = 3] = \"BIOMETRICS_NOT_ENROLLED\";\n /**\n * User is temporarily locked out (Android: 30 second lockout)\n * Platform: Android\n */\n BiometricAuthError[BiometricAuthError[\"USER_TEMPORARY_LOCKOUT\"] = 4] = \"USER_TEMPORARY_LOCKOUT\";\n /**\n * Authentication failed (user did not authenticate successfully)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"AUTHENTICATION_FAILED\"] = 10] = \"AUTHENTICATION_FAILED\";\n /**\n * App canceled the authentication (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"APP_CANCEL\"] = 11] = \"APP_CANCEL\";\n /**\n * Invalid context (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"INVALID_CONTEXT\"] = 12] = \"INVALID_CONTEXT\";\n /**\n * Authentication was not interactive (iOS only)\n * Platform: iOS\n */\n BiometricAuthError[BiometricAuthError[\"NOT_INTERACTIVE\"] = 13] = \"NOT_INTERACTIVE\";\n /**\n * Passcode/PIN is not set on the device\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"PASSCODE_NOT_SET\"] = 14] = \"PASSCODE_NOT_SET\";\n /**\n * System canceled the authentication (e.g., due to screen lock)\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"SYSTEM_CANCEL\"] = 15] = \"SYSTEM_CANCEL\";\n /**\n * User canceled the authentication\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_CANCEL\"] = 16] = \"USER_CANCEL\";\n /**\n * User chose to use fallback authentication method\n * Platform: Android, iOS\n */\n BiometricAuthError[BiometricAuthError[\"USER_FALLBACK\"] = 17] = \"USER_FALLBACK\";\n})(BiometricAuthError || (BiometricAuthError = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeBiometric = registerPlugin('NativeBiometric', {\n web: () => import('./web').then((m) => new m.NativeBiometricWeb()),\n});\nexport * from './definitions';\nexport { NativeBiometric };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { BiometryType, AuthenticationStrength } from './definitions';\nexport class NativeBiometricWeb extends WebPlugin {\n constructor() {\n super();\n /**\n * In-memory credential storage for browser development/testing.\n * Credentials are stored temporarily and cleared on page refresh.\n * This is NOT secure storage and should only be used for development purposes.\n */\n this.credentialStore = new Map();\n this.dataStore = new Map();\n }\n isAvailable() {\n // Web platform: return a dummy implementation for development/testing\n // Using TOUCH_ID as a generic placeholder for simulated biometric authentication\n return Promise.resolve({\n isAvailable: true,\n authenticationStrength: AuthenticationStrength.STRONG,\n biometryType: BiometryType.TOUCH_ID,\n deviceIsSecure: true,\n strongBiometryIsAvailable: true,\n });\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async addListener(_eventName, _listener) {\n // Web platform: no-op, but return a valid handle\n return {\n remove: async () => {\n // Nothing to remove on web\n },\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n verifyIdentity(_options) {\n console.log('verifyIdentity (dummy implementation)');\n // Dummy implementation: always succeeds for browser testing\n return Promise.resolve();\n }\n getCredentials(_options) {\n console.log('getCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n getSecureCredentials(_options) {\n console.log('getSecureCredentials (dummy implementation)', { server: _options.server });\n const credentials = this.credentialStore.get(_options.server);\n if (!credentials) {\n throw new Error('No credentials found for the specified server');\n }\n return Promise.resolve(credentials);\n }\n setCredentials(_options) {\n console.log('setCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: store in memory\n this.credentialStore.set(_options.server, {\n username: _options.username,\n password: _options.password,\n });\n return Promise.resolve();\n }\n deleteCredentials(_options) {\n console.log('deleteCredentials (dummy implementation)', { server: _options.server });\n // Dummy implementation: remove from in-memory store\n this.credentialStore.delete(_options.server);\n return Promise.resolve();\n }\n isCredentialsSaved(_options) {\n console.log('isCredentialsSaved (dummy implementation)', { server: _options.server });\n // Dummy implementation: check in-memory store\n return Promise.resolve({ isSaved: this.credentialStore.has(_options.server) });\n }\n setData(_options) {\n console.log('setData (dummy implementation)', { key: _options.key });\n this.dataStore.set(_options.key, _options.value);\n return Promise.resolve();\n }\n getData(_options) {\n console.log('getData (dummy implementation)', { key: _options.key });\n const value = this.dataStore.get(_options.key);\n if (value === undefined) {\n throw new Error('No data found for the specified key');\n }\n return Promise.resolve({ value });\n }\n getSecureData(_options) {\n console.log('getSecureData (dummy implementation)', { key: _options.key });\n const value = this.dataStore.get(_options.key);\n if (value === undefined) {\n throw new Error('No protected data found for the specified key');\n }\n return Promise.resolve({ value });\n }\n deleteData(_options) {\n console.log('deleteData (dummy implementation)', { key: _options.key });\n this.dataStore.delete(_options.key);\n return Promise.resolve();\n }\n isDataSaved(_options) {\n console.log('isDataSaved (dummy implementation)', { key: _options.key });\n return Promise.resolve({ isSaved: this.dataStore.has(_options.key) });\n }\n async getPluginVersion() {\n return { version: 'web' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BiometryType","AuthenticationStrength","AccessControl","BiometricAuthError","registerPlugin","WebPlugin"],"mappings":"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"} |
@@ -14,3 +14,3 @@ import Foundation | ||
| public class NativeBiometricPlugin: CAPPlugin, CAPBridgedPlugin { | ||
| private let pluginVersion: String = "8.5.0" | ||
| private let pluginVersion: String = "8.6.0" | ||
| public let identifier = "NativeBiometricPlugin" | ||
@@ -26,2 +26,7 @@ public let jsName = "NativeBiometric" | ||
| CAPPluginMethod(name: "isCredentialsSaved", returnType: CAPPluginReturnPromise), | ||
| CAPPluginMethod(name: "setData", returnType: CAPPluginReturnPromise), | ||
| CAPPluginMethod(name: "getData", returnType: CAPPluginReturnPromise), | ||
| CAPPluginMethod(name: "getSecureData", returnType: CAPPluginReturnPromise), | ||
| CAPPluginMethod(name: "deleteData", returnType: CAPPluginReturnPromise), | ||
| CAPPluginMethod(name: "isDataSaved", returnType: CAPPluginReturnPromise), | ||
| CAPPluginMethod(name: "getPluginVersion", returnType: CAPPluginReturnPromise) | ||
@@ -303,2 +308,287 @@ ] | ||
| private let dataService = "CapgoNativeBiometricData" | ||
| private let secureDataService = "CapgoNativeBiometricSecureData" | ||
| private func dataAccount(_ key: String) -> String { | ||
| return key | ||
| } | ||
| @objc func setData(_ call: CAPPluginCall) { | ||
| guard let key = call.getString("key"), let value = call.getString("value") else { | ||
| call.reject("Missing properties") | ||
| return | ||
| } | ||
| let accessControl = call.getInt("accessControl") ?? 0 | ||
| if accessControl > 0 { | ||
| do { | ||
| try storeProtectedData(value, key, accessControl) | ||
| call.resolve() | ||
| } catch KeychainError.duplicateItem { | ||
| do { | ||
| try deleteProtectedData(key) | ||
| try storeProtectedData(value, key, accessControl) | ||
| call.resolve() | ||
| } catch { | ||
| call.reject(error.localizedDescription) | ||
| } | ||
| } catch { | ||
| call.reject(error.localizedDescription) | ||
| } | ||
| } else { | ||
| do { | ||
| try storeDataInKeychain(value, key) | ||
| call.resolve() | ||
| } catch KeychainError.duplicateItem { | ||
| do { | ||
| try updateDataInKeychain(value, key) | ||
| call.resolve() | ||
| } catch { | ||
| call.reject(error.localizedDescription) | ||
| } | ||
| } catch { | ||
| call.reject(error.localizedDescription) | ||
| } | ||
| } | ||
| } | ||
| @objc func getData(_ call: CAPPluginCall) { | ||
| guard let key = call.getString("key") else { | ||
| call.reject("No key was provided") | ||
| return | ||
| } | ||
| do { | ||
| let value = try getDataFromKeychain(key) | ||
| var obj = JSObject() | ||
| obj["value"] = value | ||
| call.resolve(obj) | ||
| } catch { | ||
| call.reject(error.localizedDescription) | ||
| } | ||
| } | ||
| @objc func getSecureData(_ call: CAPPluginCall) { | ||
| guard let key = call.getString("key") else { | ||
| call.reject("No key was provided") | ||
| return | ||
| } | ||
| let context = LAContext() | ||
| if let reason = call.getString("reason") { | ||
| context.localizedReason = reason | ||
| } | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: secureDataService, | ||
| kSecAttrAccount as String: dataAccount(key), | ||
| kSecMatchLimit as String: kSecMatchLimitOne, | ||
| kSecReturnAttributes as String: true, | ||
| kSecReturnData as String: true, | ||
| kSecUseAuthenticationContext as String: context | ||
| ] | ||
| DispatchQueue.global(qos: .userInitiated).async { | ||
| var item: CFTypeRef? | ||
| let status = SecItemCopyMatching(query as CFDictionary, &item) | ||
| DispatchQueue.main.async { | ||
| if status == errSecUserCanceled { | ||
| call.reject("User canceled biometric authentication", "16") | ||
| return | ||
| } | ||
| guard status == errSecSuccess else { | ||
| if status == errSecItemNotFound { | ||
| call.reject("No protected data found for key", "21") | ||
| } else if status == errSecAuthFailed { | ||
| call.reject("Biometric authentication failed", "10") | ||
| } else { | ||
| call.reject("Failed to retrieve data: \(status)", "0") | ||
| } | ||
| return | ||
| } | ||
| guard let existingItem = item as? [String: Any], | ||
| let valueData = existingItem[kSecValueData as String] as? Data, | ||
| let value = String(data: valueData, encoding: .utf8) | ||
| else { | ||
| call.reject("Unexpected data format") | ||
| return | ||
| } | ||
| var obj = JSObject() | ||
| obj["value"] = value | ||
| call.resolve(obj) | ||
| } | ||
| } | ||
| } | ||
| @objc func deleteData(_ call: CAPPluginCall) { | ||
| guard let key = call.getString("key") else { | ||
| call.reject("No key was provided") | ||
| return | ||
| } | ||
| do { | ||
| try deleteDataFromKeychain(key) | ||
| try deleteProtectedData(key) | ||
| call.resolve() | ||
| } catch { | ||
| call.reject(error.localizedDescription) | ||
| } | ||
| } | ||
| @objc func isDataSaved(_ call: CAPPluginCall) { | ||
| guard let key = call.getString("key") else { | ||
| call.reject("No key was provided") | ||
| return | ||
| } | ||
| var obj = JSObject() | ||
| obj["isSaved"] = checkDataExist(key) || checkProtectedDataExist(key) | ||
| call.resolve(obj) | ||
| } | ||
| func storeDataInKeychain(_ value: String, _ key: String) throws { | ||
| guard let valueData = value.data(using: .utf8) else { | ||
| throw KeychainError.unexpectedPasswordData | ||
| } | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: dataService, | ||
| kSecAttrAccount as String: dataAccount(key), | ||
| kSecValueData as String: valueData | ||
| ] | ||
| let status = SecItemAdd(query as CFDictionary, nil) | ||
| guard status != errSecDuplicateItem else { throw KeychainError.duplicateItem } | ||
| guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) } | ||
| } | ||
| func updateDataInKeychain(_ value: String, _ key: String) throws { | ||
| guard let valueData = value.data(using: .utf8) else { | ||
| throw KeychainError.unexpectedPasswordData | ||
| } | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: dataService, | ||
| kSecAttrAccount as String: dataAccount(key) | ||
| ] | ||
| let attributes: [String: Any] = [kSecValueData as String: valueData] | ||
| let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) | ||
| guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) } | ||
| } | ||
| func getDataFromKeychain(_ key: String) throws -> String { | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: dataService, | ||
| kSecAttrAccount as String: dataAccount(key), | ||
| kSecMatchLimit as String: kSecMatchLimitOne, | ||
| kSecReturnAttributes as String: true, | ||
| kSecReturnData as String: true | ||
| ] | ||
| var item: CFTypeRef? | ||
| let status = SecItemCopyMatching(query as CFDictionary, &item) | ||
| guard status != errSecItemNotFound else { throw KeychainError.noPassword } | ||
| guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) } | ||
| guard let existingItem = item as? [String: Any], | ||
| let valueData = existingItem[kSecValueData as String] as? Data, | ||
| let value = String(data: valueData, encoding: .utf8) | ||
| else { | ||
| throw KeychainError.unexpectedPasswordData | ||
| } | ||
| return value | ||
| } | ||
| func deleteDataFromKeychain(_ key: String) throws { | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: dataService, | ||
| kSecAttrAccount as String: dataAccount(key) | ||
| ] | ||
| let status = SecItemDelete(query as CFDictionary) | ||
| guard status == errSecSuccess || status == errSecItemNotFound else { | ||
| throw KeychainError.unhandledError(status: status) | ||
| } | ||
| } | ||
| func storeProtectedData(_ value: String, _ key: String, _ accessControl: Int) throws { | ||
| guard let valueData = value.data(using: .utf8) else { | ||
| throw KeychainError.unexpectedPasswordData | ||
| } | ||
| let flags: SecAccessControlCreateFlags = accessControl == 1 ? .biometryCurrentSet : .biometryAny | ||
| guard let access = SecAccessControlCreateWithFlags( | ||
| kCFAllocatorDefault, | ||
| kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, | ||
| flags, | ||
| nil | ||
| ) else { | ||
| throw KeychainError.unhandledError(status: errSecParam) | ||
| } | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: secureDataService, | ||
| kSecAttrAccount as String: dataAccount(key), | ||
| kSecValueData as String: valueData, | ||
| kSecAttrAccessControl as String: access | ||
| ] | ||
| let status = SecItemAdd(query as CFDictionary, nil) | ||
| guard status != errSecDuplicateItem else { throw KeychainError.duplicateItem } | ||
| guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) } | ||
| } | ||
| func deleteProtectedData(_ key: String) throws { | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: secureDataService, | ||
| kSecAttrAccount as String: dataAccount(key) | ||
| ] | ||
| let status = SecItemDelete(query as CFDictionary) | ||
| guard status == errSecSuccess || status == errSecItemNotFound else { | ||
| throw KeychainError.unhandledError(status: status) | ||
| } | ||
| } | ||
| func checkDataExist(_ key: String) -> Bool { | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: dataService, | ||
| kSecAttrAccount as String: dataAccount(key), | ||
| kSecMatchLimit as String: kSecMatchLimitOne, | ||
| kSecReturnAttributes as String: true | ||
| ] | ||
| var item: CFTypeRef? | ||
| let status = SecItemCopyMatching(query as CFDictionary, &item) | ||
| return status == errSecSuccess | ||
| } | ||
| func checkProtectedDataExist(_ key: String) -> Bool { | ||
| let query: [String: Any] = [ | ||
| kSecClass as String: kSecClassGenericPassword, | ||
| kSecAttrService as String: secureDataService, | ||
| kSecAttrAccount as String: dataAccount(key), | ||
| kSecMatchLimit as String: kSecMatchLimitOne, | ||
| kSecReturnAttributes as String: true | ||
| ] | ||
| var item: CFTypeRef? | ||
| let status = SecItemCopyMatching(query as CFDictionary, &item) | ||
| return status == errSecSuccess | ||
| } | ||
| @objc func isCredentialsSaved(_ call: CAPPluginCall) { | ||
@@ -305,0 +595,0 @@ guard let server = call.getString("server") else { |
+1
-1
| { | ||
| "name": "@capgo/capacitor-native-biometric", | ||
| "version": "8.5.0", | ||
| "version": "8.6.0", | ||
| "description": "This plugin gives access to the native biometric apis for android and iOS", | ||
@@ -5,0 +5,0 @@ "main": "dist/plugin.cjs.js", |
+181
-19
@@ -302,2 +302,7 @@ # Capacitor Native Biometric | ||
| * [`isCredentialsSaved(...)`](#iscredentialssaved) | ||
| * [`setData(...)`](#setdata) | ||
| * [`getData(...)`](#getdata) | ||
| * [`getSecureData(...)`](#getsecuredata) | ||
| * [`deleteData(...)`](#deletedata) | ||
| * [`isDataSaved(...)`](#isdatasaved) | ||
| * [`getPluginVersion()`](#getpluginversion) | ||
@@ -466,2 +471,100 @@ * [Interfaces](#interfaces) | ||
| ### setData(...) | ||
| ```typescript | ||
| setData(options: SetDataOptions) => Promise<void> | ||
| ``` | ||
| Stores an arbitrary string value under the given key. | ||
| Values are encrypted at rest using the platform secure storage backend | ||
| (Android Keystore + SharedPreferences, iOS Keychain). | ||
| For biometric-protected storage, set `accessControl` and retrieve the value | ||
| with `getSecureData()`. Credential helpers remain available for username/password flows. | ||
| | Param | Type | | ||
| | ------------- | --------------------------------------------------------- | | ||
| | **`options`** | <code><a href="#setdataoptions">SetDataOptions</a></code> | | ||
| **Since:** 8.6.0 | ||
| -------------------- | ||
| ### getData(...) | ||
| ```typescript | ||
| getData(options: GetDataOptions) => Promise<StoredData> | ||
| ``` | ||
| Gets a previously stored value for the given key. | ||
| Only returns values stored without biometric `accessControl`. | ||
| | Param | Type | | ||
| | ------------- | --------------------------------------------------------- | | ||
| | **`options`** | <code><a href="#getdataoptions">GetDataOptions</a></code> | | ||
| **Returns:** <code>Promise<<a href="#storeddata">StoredData</a>></code> | ||
| **Since:** 8.6.0 | ||
| -------------------- | ||
| ### getSecureData(...) | ||
| ```typescript | ||
| getSecureData(options: GetSecureDataOptions) => Promise<StoredData> | ||
| ``` | ||
| Gets a biometric-protected value for the given key. | ||
| The value must have been stored with `accessControl` set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY. | ||
| | Param | Type | | ||
| | ------------- | --------------------------------------------------------------------- | | ||
| | **`options`** | <code><a href="#getsecuredataoptions">GetSecureDataOptions</a></code> | | ||
| **Returns:** <code>Promise<<a href="#storeddata">StoredData</a>></code> | ||
| **Since:** 8.6.0 | ||
| -------------------- | ||
| ### deleteData(...) | ||
| ```typescript | ||
| deleteData(options: DeleteDataOptions) => Promise<void> | ||
| ``` | ||
| Deletes the stored value for the given key (protected and unprotected). | ||
| | Param | Type | | ||
| | ------------- | --------------------------------------------------------------- | | ||
| | **`options`** | <code><a href="#deletedataoptions">DeleteDataOptions</a></code> | | ||
| **Since:** 8.6.0 | ||
| -------------------- | ||
| ### isDataSaved(...) | ||
| ```typescript | ||
| isDataSaved(options: IsDataSavedOptions) => Promise<IsDataSavedResult> | ||
| ``` | ||
| Checks whether a value is already saved for the given key. | ||
| | Param | Type | | ||
| | ------------- | ----------------------------------------------------------------- | | ||
| | **`options`** | <code><a href="#isdatasavedoptions">IsDataSavedOptions</a></code> | | ||
| **Returns:** <code>Promise<<a href="#isdatasavedresult">IsDataSavedResult</a>></code> | ||
| **Since:** 8.6.0 | ||
| -------------------- | ||
| ### getPluginVersion() | ||
@@ -489,10 +592,10 @@ | ||
| | Prop | Type | Description | | ||
| | ------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **`isAvailable`** | <code>boolean</code> | Whether authentication is available. On Android, `verifyIdentity()` uses a `CryptoObject` backed by `BIOMETRIC_STRONG`, so weak-only biometric methods such as some face unlock implementations are reported via `biometryType`/`authenticationStrength` but do not make this value `true`. If `useFallback` is true, PIN/pattern/password can make this value true. | | ||
| | **`authenticationStrength`** | <code><a href="#authenticationstrength">AuthenticationStrength</a></code> | The strength of available authentication method (STRONG, WEAK, or NONE) | | ||
| | **`biometryType`** | <code><a href="#biometrytype">BiometryType</a></code> | The primary biometry type available on the device. On Android devices with multiple biometry types, this returns MULTIPLE. Use this for display purposes only - always use isAvailable for logic decisions. | | ||
| | **`deviceIsSecure`** | <code>boolean</code> | Whether the device has a secure lock screen (PIN, pattern, or password). This is independent of biometric enrollment. | | ||
| | **`strongBiometryIsAvailable`** | <code>boolean</code> | Whether strong biometry (Face ID, Touch ID, or fingerprint on devices that consider it strong) is specifically available, separate from weak biometry or device credentials. | | ||
| | **`errorCode`** | <code><a href="#biometricautherror">BiometricAuthError</a></code> | Error code from <a href="#biometricautherror">BiometricAuthError</a> enum. Only present when isAvailable is false. Indicates why biometric authentication is not available. | | ||
| | Prop | Type | Description | | ||
| | ------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **`isAvailable`** | <code>boolean</code> | Whether authentication is available. On Android, weak-only biometrics (such as some face unlock implementations) are available for `verifyIdentity()` when no `allowedBiometryTypes` filter excludes them. If `useFallback` is true, PIN/pattern/password can also make this value true. | | ||
| | **`authenticationStrength`** | <code><a href="#authenticationstrength">AuthenticationStrength</a></code> | The strength of available authentication method (STRONG, WEAK, or NONE) | | ||
| | **`biometryType`** | <code><a href="#biometrytype">BiometryType</a></code> | The primary biometry type available on the device. On Android devices with multiple biometry types, this returns MULTIPLE. Use this for display purposes only - always use isAvailable for logic decisions. | | ||
| | **`deviceIsSecure`** | <code>boolean</code> | Whether the device has a secure lock screen (PIN, pattern, or password). This is independent of biometric enrollment. | | ||
| | **`strongBiometryIsAvailable`** | <code>boolean</code> | Whether strong biometry (Face ID, Touch ID, or fingerprint on devices that consider it strong) is specifically available, separate from weak biometry or device credentials. | | ||
| | **`errorCode`** | <code><a href="#biometricautherror">BiometricAuthError</a></code> | Error code from <a href="#biometricautherror">BiometricAuthError</a> enum. Only present when isAvailable is false. Indicates why biometric authentication is not available. | | ||
@@ -516,13 +619,13 @@ | ||
| | Prop | Type | Description | Default | | ||
| | -------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | ||
| | **`reason`** | <code>string</code> | | | | ||
| | **`title`** | <code>string</code> | | | | ||
| | **`subtitle`** | <code>string</code> | | | | ||
| | **`description`** | <code>string</code> | | | | ||
| | **`negativeButtonText`** | <code>string</code> | | | | ||
| | **`useFallback`** | <code>boolean</code> | Only for iOS. Specifies if should fallback to passcode authentication if biometric authentication fails. On Android, this parameter is ignored due to BiometricPrompt API constraints: DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive. | | | ||
| | **`fallbackTitle`** | <code>string</code> | Only for iOS. Set the text for the fallback button in the authentication dialog. If this property is not specified, the default text is set by the system. | | | ||
| | **`maxAttempts`** | <code>number</code> | Only for Android. Set a maximum number of attempts for biometric authentication. The maximum allowed by android is 5. | <code>1</code> | | ||
| | **`allowedBiometryTypes`** | <code>BiometryType[]</code> | Only for Android. Specify which biometry types are allowed for authentication. If not specified, all available types will be allowed. | | | ||
| | Prop | Type | Description | Default | | ||
| | -------------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | ||
| | **`reason`** | <code>string</code> | | | | ||
| | **`title`** | <code>string</code> | | | | ||
| | **`subtitle`** | <code>string</code> | | | | ||
| | **`description`** | <code>string</code> | | | | ||
| | **`negativeButtonText`** | <code>string</code> | | | | ||
| | **`useFallback`** | <code>boolean</code> | Only for iOS. Specifies if should fallback to passcode authentication if biometric authentication fails. On Android, this parameter is ignored due to BiometricPrompt API constraints: DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive. | | | ||
| | **`fallbackTitle`** | <code>string</code> | Only for iOS. Set the text for the fallback button in the authentication dialog. If this property is not specified, the default text is set by the system. | | | ||
| | **`maxAttempts`** | <code>number</code> | Only for Android. Set a maximum number of attempts for biometric authentication. The maximum allowed by android is 5. | <code>1</code> | | ||
| | **`allowedBiometryTypes`** | <code>BiometryType[]</code> | Only for Android. Specify which biometry types are allowed for authentication. If not specified, all enrolled biometric classes (strong and weak) are allowed. On Android, face unlock is often classified as weak biometrics — include `BiometryType.FACE_AUTHENTICATION` or omit this option to allow it. Use <a href="#biometrytype">`BiometryType.DEVICE_CREDENTIAL`</a> for PIN/pattern/password (disables the cancel button). | | | ||
@@ -591,2 +694,61 @@ | ||
| #### SetDataOptions | ||
| | Prop | Type | Description | Default | Since | | ||
| | -------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----- | | ||
| | **`key`** | <code>string</code> | Unique identifier for the stored value. Use a stable app-specific namespace (e.g. `pin`, `session.token`). | | | | ||
| | **`value`** | <code>string</code> | Arbitrary string payload. Serialize objects with `JSON.stringify()` before storing. Platform limits apply: Android Keystore-backed encryption works best with payloads under ~8 KB; iOS Keychain practical limits are higher but very large values are discouraged. | | | | ||
| | **`accessControl`** | <code><a href="#accesscontrol">AccessControl</a></code> | Access control level for the stored value. When set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the value is hardware-protected and requires biometric authentication to access via `getSecureData()`. | <code>AccessControl.NONE</code> | 8.6.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. | <code>0</code> | 8.6.0 | | ||
| | **`title`** | <code>string</code> | Title for the biometric prompt shown while protecting data. Only for Android. | <code>"Protect Data"</code> | 8.6.0 | | ||
| | **`negativeButtonText`** | <code>string</code> | Text for the negative/cancel button in the biometric prompt. Only for Android. | <code>"Cancel"</code> | 8.6.0 | | ||
| #### StoredData | ||
| | Prop | Type | | ||
| | ----------- | ------------------- | | ||
| | **`value`** | <code>string</code> | | ||
| #### GetDataOptions | ||
| | Prop | Type | | ||
| | --------- | ------------------- | | ||
| | **`key`** | <code>string</code> | | ||
| #### GetSecureDataOptions | ||
| | Prop | Type | Description | | ||
| | ------------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------- | | ||
| | **`key`** | <code>string</code> | | | ||
| | **`reason`** | <code>string</code> | Reason for requesting biometric authentication. Displayed in the biometric prompt on both iOS and Android. | | ||
| | **`title`** | <code>string</code> | Title for the biometric prompt. Only for Android. | | ||
| | **`subtitle`** | <code>string</code> | Subtitle for the biometric prompt. Only for Android. | | ||
| | **`description`** | <code>string</code> | Description for the biometric prompt. Only for Android. | | ||
| | **`negativeButtonText`** | <code>string</code> | Text for the negative/cancel button. Only for Android. | | ||
| #### DeleteDataOptions | ||
| | Prop | Type | | ||
| | --------- | ------------------- | | ||
| | **`key`** | <code>string</code> | | ||
| #### IsDataSavedResult | ||
| | Prop | Type | | ||
| | ------------- | -------------------- | | ||
| | **`isSaved`** | <code>boolean</code> | | ||
| #### IsDataSavedOptions | ||
| | Prop | Type | | ||
| | --------- | ------------------- | | ||
| | **`key`** | <code>string</code> | | ||
| ### Type Aliases | ||
@@ -593,0 +755,0 @@ |
351484
23.46%33
3.13%4231
28.99%861
23.18%