From aa7a7684c4767eafca96f37348b0ad8b65f3bc16 Mon Sep 17 00:00:00 2001 From: SaeedZhiany Date: Thu, 16 Apr 2020 11:45:54 +0430 Subject: [PATCH 01/21] fixed Gradle setup Loaded Android Gradle Plugin conditionally removed buildToolsVersion, because it's no longer needed to specify --- android/build.gradle | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 053c455..09cf1bf 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,29 +2,32 @@ apply plugin: 'com.android.library' description = 'react-native-biometrics' -def DEFAULT_COMPILE_SDK_VERSION = 29 -def DEFAULT_BUILD_TOOLS_VERSION = "29.0.2" -def DEFAULT_MIN_SDK_VERSION = 16 -def DEFAULT_TARGET_SDK_VERSION = 29 - buildscript { - repositories { - google() - jcenter() + // The Android Gradle plugin is only required when opening the android folder stand-alone. + // This avoids unnecessary downloads and potential conflicts when the library is included as a + // module dependency in an application project. + if (project == rootProject) { + repositories { + google() + jcenter() + } + + dependencies { + classpath("com.android.tools.build:gradle:3.6.2") + } } +} - dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' - } +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback } android { - compileSdkVersion rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION - buildToolsVersion rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEFAULT_BUILD_TOOLS_VERSION + compileSdkVersion safeExtGet('compileSdkVersion', 29) defaultConfig { - minSdkVersion rootProject.hasProperty('minSdkVersion') ? rootProject.minSdkVersion : DEFAULT_MIN_SDK_VERSION - targetSdkVersion rootProject.hasProperty('targetSdkVersion') ? rootProject.targetSdkVersion : DEFAULT_TARGET_SDK_VERSION + minSdkVersion safeExtGet('minSdkVersion', 16) + targetSdkVersion safeExtGet('targetSdkVersion', 29) } lintOptions { abortOnError false From e430f74cf3529d3eb371ae4342665e75ab1af201 Mon Sep 17 00:00:00 2001 From: Jin Shin Date: Thu, 1 Oct 2020 10:01:31 +0900 Subject: [PATCH 02/21] Fix Xcode 12 compatibility. --- react-native-biometrics.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-native-biometrics.podspec b/react-native-biometrics.podspec index b451efa..adc0a7f 100644 --- a/react-native-biometrics.podspec +++ b/react-native-biometrics.podspec @@ -13,5 +13,5 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/SelfLender/react-native-biometrics.git', :tag => "#{s.version}" } s.platform = :ios, '10.0' s.source_files = 'ios/**/*.{h,m}' - s.dependency 'React' + s.dependency 'React-Core' end From 4168db50152ab4624aca620bdfe6e50847b3ae2e Mon Sep 17 00:00:00 2001 From: Jason Merino Date: Thu, 19 Nov 2020 11:52:02 -0800 Subject: [PATCH 03/21] Remove unused string from call to createKeys in docs Looking at source for the `createKeys` method, it seems like it doesn't do anything with any arguments. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9af669b..3c50866 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -ReactNativeBiometrics.createKeys('Confirm fingerprint') +ReactNativeBiometrics.createKeys() .then((resultObject) => { const { publicKey } = resultObject console.log(publicKey) From e8ba55d8fa632fdd15b98727dc6aa76685bbdbce Mon Sep 17 00:00:00 2001 From: Albert Buchard Date: Sat, 4 Sep 2021 01:20:59 +0200 Subject: [PATCH 04/21] Update README.md Added pod install which seems necessary to install properly on ios --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9af669b..5935d7e 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ or npm: `$ npm install react-native-biometrics --save` +### Install pods + +`$ npx pod-install` + ### Link / Autolinking On React Native 0.60+ the [CLI autolink feature](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md) links the module while building the app. From bd2b3aaece966eb0e73dc5a738df9d2375eb9c66 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Thu, 10 Mar 2022 10:20:42 +0200 Subject: [PATCH 05/21] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9af669b..6410a84 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ On React Native 0.60+ the [CLI autolink feature](https://github.com/react-native This package requires an iOS target SDK version of iOS 10 or higher -Ensure that you have the `NSFaceIDUsageDescription` entry set in your react native iOS project, or Face ID will not work properly. This description will be will be presented to the user the first time a biometrics action is taken, and the user will be asked if they want to allow the app to use Face ID. If the user declines the usage of face id for the app, the `isSensorAvailable` function will indicate biometrics is unavailable until the face id permission is specifically allowed for the app by the user. +Ensure that you have the `NSFaceIDUsageDescription` entry set in your react native iOS project, or Face ID will not work properly. This description will be presented to the user the first time a biometrics action is taken, and the user will be asked if they want to allow the app to use Face ID. If the user declines the usage of face id for the app, the `isSensorAvailable` function will indicate biometrics is unavailable until the face id permission is specifically allowed for the app by the user. #### Android From 7b3023123899e059713dbb705e669ec59031a385 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Mon, 9 May 2022 13:54:06 -0500 Subject: [PATCH 06/21] Bumping version to 2.2.0 in preparation for the future release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e48f6d8..8071d5d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-biometrics", - "version": "2.1.4", + "version": "2.2.0", "summary": "A React Native library for biometrics", "description": "React Native biometric functionality for signing and encryption", "main": "build/cjs/index.js", From f0657654aef039c30f50efb8987e27b6babc3eed Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Mon, 9 May 2022 13:54:45 -0500 Subject: [PATCH 07/21] Added passcode fallback for iOS devices when FaceID or TouchID fails and the device has a passcode set. --- README.md | 1 + index.ts | 5 +++++ ios/ReactNativeBiometrics.m | 9 +++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9af669b..aaf3564 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,7 @@ __Options Object__ | Parameter | Type | Description | iOS | Android | | --- | --- | --- | --- | --- | | promptMessage | string | Message that will be displayed in the biometrics prompt | ✔ | ✔ | +| fallbackPromptMessage | string | Message that will be shown when FaceID or TouchID has failed and a passcode has been set on the device. | ✔ | ✖ | | cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | __Result Object__ diff --git a/index.ts b/index.ts index 8524bbc..e1e781f 100644 --- a/index.ts +++ b/index.ts @@ -39,6 +39,7 @@ interface CreateSignatureResult { interface SimplePromptOptions { promptMessage: string + fallbackPromptMessage?: string cancelButtonText?: string } @@ -127,6 +128,10 @@ module ReactNativeBiometrics { simplePromptOptions.cancelButtonText = 'Cancel'; } + if (!simplePromptOptions.fallbackPromptMessage) { + simplePromptOptions.cancelButtonText = 'Use Passcode'; + } + return bridge.simplePrompt(simplePromptOptions); } } diff --git a/ios/ReactNativeBiometrics.m b/ios/ReactNativeBiometrics.m index e1aa016..db0c24a 100644 --- a/ios/ReactNativeBiometrics.m +++ b/ios/ReactNativeBiometrics.m @@ -17,7 +17,7 @@ @implementation ReactNativeBiometrics { LAContext *context = [[LAContext alloc] init]; NSError *la_error = nil; - BOOL canEvaluatePolicy = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&la_error]; + BOOL canEvaluatePolicy = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&la_error]; if (canEvaluatePolicy) { NSString *biometryType = [self getBiometryType:context]; @@ -44,7 +44,7 @@ @implementation ReactNativeBiometrics SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, - kSecAccessControlBiometryAny, &error); + kSecAccessControlUserPresence, &error); if (sacObject == NULL || error != NULL) { NSString *errorString = [NSString stringWithFormat:@"SecItemAdd can't create sacObject: %@", error]; reject(@"storage_error", errorString, nil); @@ -159,11 +159,12 @@ @implementation ReactNativeBiometrics RCT_EXPORT_METHOD(simplePrompt: (NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *promptMessage = [RCTConvert NSString:params[@"promptMessage"]]; + NSString *fallbackPromptMessage = [RCTConvert NSString:params[@"fallbackPromptMessage"]]; LAContext *context = [[LAContext alloc] init]; - context.localizedFallbackTitle = @""; + context.localizedFallbackTitle = fallbackPromptMessage; - [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:promptMessage reply:^(BOOL success, NSError *biometricError) { + [context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:promptMessage reply:^(BOOL success, NSError *biometricError) { if (success) { NSDictionary *result = @{ @"success": @(YES) From 4ebe295d1e4949165910cb8a62ab04f3232e83f1 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Fri, 13 May 2022 17:06:17 -0500 Subject: [PATCH 08/21] Upgraded AndroidX Biometric library to 1.1.0 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 09cf1bf..82f910d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -40,6 +40,6 @@ repositories { } dependencies { - implementation 'androidx.biometric:biometric:1.0.1' + implementation 'androidx.biometric:biometric:1.1.0' implementation 'com.facebook.react:react-native:+' } From 2e8bc983bb1c79559ff2ebd6f1132b3e67307595 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Fri, 13 May 2022 17:10:28 -0500 Subject: [PATCH 09/21] Removed cancelButtonText option because this capability is being deprecated in 1.1.0 version of AndroidX Biometric in favor of an OS directed text Added allowDeviceCredentials to provide users of the library the capability for their users to fallback to the device credentials Updated createSignature and simplePrompt implementations to use the preferred 1.1.0 AndroidX Biometric approach for prompting the user. --- README.md | 4 +- .../rnbiometrics/ReactNativeBiometrics.java | 65 +++++++++++-------- index.ts | 20 ++---- 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 83d6574..0858e5d 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,7 @@ __Options Object__ | --- | --- | --- | --- | --- | | promptMessage | string | Message that will be displayed in the fingerprint or face id prompt | ✔ | ✔ | | payload | string | String of data to be signed by the RSA signature | ✔ | ✔ | -| cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | +| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. | ✖ | ✔ | __Result Object__ @@ -266,7 +266,7 @@ __Options Object__ | --- | --- | --- | --- | --- | | promptMessage | string | Message that will be displayed in the biometrics prompt | ✔ | ✔ | | fallbackPromptMessage | string | Message that will be shown when FaceID or TouchID has failed and a passcode has been set on the device. | ✔ | ✖ | -| cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | +| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. | ✖ | ✔ | __Result Object__ diff --git a/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java b/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java index 790a373..e28eb7d 100644 --- a/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java +++ b/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java @@ -50,7 +50,7 @@ public String getName() { @ReactMethod public void isSensorAvailable(Promise promise) { try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (isCurrentSDKMarshmallowOrLater()) { ReactApplicationContext reactApplicationContext = getReactApplicationContext(); BiometricManager biometricManager = BiometricManager.from(reactApplicationContext); int canAuthenticate = biometricManager.canAuthenticate(); @@ -92,7 +92,7 @@ public void isSensorAvailable(Promise promise) { @ReactMethod public void createKeys(Promise promise) { try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (isCurrentSDKMarshmallowOrLater()) { deleteBiometricKey(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(biometricKeyAlias, KeyProperties.PURPOSE_SIGN) @@ -120,6 +120,10 @@ public void createKeys(Promise promise) { } } + private boolean isCurrentSDKMarshmallowOrLater() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; + } + @ReactMethod public void deleteKeys(Promise promise) { if (doesBiometricKeyExist()) { @@ -141,15 +145,15 @@ public void deleteKeys(Promise promise) { @ReactMethod public void createSignature(final ReadableMap params, final Promise promise) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (isCurrentSDKMarshmallowOrLater()) { UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { try { - String cancelButtomText = params.getString("cancelButtonText"); String promptMessage = params.getString("promptMessage"); String payload = params.getString("payload"); + boolean allowDeviceCredentials = params.getBoolean("allowDeviceCredentials"); Signature signature = Signature.getInstance("SHA256withRSA"); KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); @@ -165,12 +169,7 @@ public void run() { Executor executor = Executors.newSingleThreadExecutor(); BiometricPrompt biometricPrompt = new BiometricPrompt(fragmentActivity, executor, authCallback); - PromptInfo promptInfo = new PromptInfo.Builder() - .setDeviceCredentialAllowed(false) - .setNegativeButtonText(cancelButtomText) - .setTitle(promptMessage) - .build(); - biometricPrompt.authenticate(promptInfo, cryptoObject); + biometricPrompt.authenticate(getPromptInfo(promptMessage, allowDeviceCredentials), cryptoObject); } catch (Exception e) { promise.reject("Error signing payload: " + e.getMessage(), "Error generating signature: " + e.getMessage()); } @@ -181,28 +180,42 @@ public void run() { } } + private PromptInfo getPromptInfo(String promptMessage, boolean allowDeviceCredentials) { + PromptInfo.Builder builder = new PromptInfo.Builder() + .setAllowedAuthenticators(getSupportedAuthenticators(allowDeviceCredentials)) + .setTitle(promptMessage); + + if (allowDeviceCredentials == false) { + builder.setNegativeButtonText("Cancel"); + } + + return builder.build(); + } + + private int getSupportedAuthenticators(boolean allowDeviceCredentials) { + if (allowDeviceCredentials) { + return BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL; + } + return BiometricManager.Authenticators.BIOMETRIC_STRONG; + } + @ReactMethod public void simplePrompt(final ReadableMap params, final Promise promise) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (isCurrentSDKMarshmallowOrLater()) { UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { try { - String cancelButtomText = params.getString("cancelButtonText"); String promptMessage = params.getString("promptMessage"); + boolean allowDeviceCredentials = params.getBoolean("allowDeviceCredentials"); AuthenticationCallback authCallback = new SimplePromptCallback(promise); FragmentActivity fragmentActivity = (FragmentActivity) getCurrentActivity(); Executor executor = Executors.newSingleThreadExecutor(); BiometricPrompt biometricPrompt = new BiometricPrompt(fragmentActivity, executor, authCallback); - PromptInfo promptInfo = new PromptInfo.Builder() - .setDeviceCredentialAllowed(false) - .setNegativeButtonText(cancelButtomText) - .setTitle(promptMessage) - .build(); - biometricPrompt.authenticate(promptInfo); + biometricPrompt.authenticate(getPromptInfo(promptMessage, allowDeviceCredentials)); } catch (Exception e) { promise.reject("Error displaying local biometric prompt: " + e.getMessage(), "Error displaying local biometric prompt: " + e.getMessage()); } @@ -226,14 +239,14 @@ public void biometricKeysExist(Promise promise) { } protected boolean doesBiometricKeyExist() { - try { - KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); - keyStore.load(null); - - return keyStore.containsAlias(biometricKeyAlias); - } catch (Exception e) { - return false; - } + try { + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + + return keyStore.containsAlias(biometricKeyAlias); + } catch (Exception e) { + return false; + } } protected boolean deleteBiometricKey() { diff --git a/index.ts b/index.ts index e1e781f..2feacfe 100644 --- a/index.ts +++ b/index.ts @@ -28,7 +28,7 @@ interface DeleteKeysResult { interface CreateSignatureOptions { promptMessage: string payload: string - cancelButtonText?: string + allowDeviceCredentials?: boolean } interface CreateSignatureResult { @@ -40,7 +40,7 @@ interface CreateSignatureResult { interface SimplePromptOptions { promptMessage: string fallbackPromptMessage?: string - cancelButtonText?: string + allowDeviceCredentials?: boolean } interface SimplePromptResult { @@ -103,13 +103,10 @@ module ReactNativeBiometrics { * @param {Object} createSignatureOptions * @param {string} createSignatureOptions.promptMessage * @param {string} createSignatureOptions.payload - * @param {string} createSignatureOptions.cancelButtonText (Android only) * @returns {Promise} Promise that resolves to an object cryptographic signature details */ export function createSignature(createSignatureOptions: CreateSignatureOptions): Promise { - if (!createSignatureOptions.cancelButtonText) { - createSignatureOptions.cancelButtonText = 'Cancel'; - } + createSignatureOptions.allowDeviceCredentials = createSignatureOptions.allowDeviceCredentials ?? false return bridge.createSignature(createSignatureOptions); } @@ -120,17 +117,12 @@ module ReactNativeBiometrics { * object.success = false if the user cancels, and rejects if anything fails * @param {Object} simplePromptOptions * @param {string} simplePromptOptions.promptMessage - * @param {string} simplePromptOptions.cancelButtonText (Android only) + * @param {string} simplePromptOptions.fallbackPromptMessage * @returns {Promise} Promise that resolves an object with details about the biometrics result */ export function simplePrompt(simplePromptOptions: SimplePromptOptions): Promise { - if (!simplePromptOptions.cancelButtonText) { - simplePromptOptions.cancelButtonText = 'Cancel'; - } - - if (!simplePromptOptions.fallbackPromptMessage) { - simplePromptOptions.cancelButtonText = 'Use Passcode'; - } + simplePromptOptions.fallbackPromptMessage = simplePromptOptions.fallbackPromptMessage ?? 'Use Passcode'; + simplePromptOptions.allowDeviceCredentials = simplePromptOptions.allowDeviceCredentials ?? false return bridge.simplePrompt(simplePromptOptions); } From fd941cd9c422d2dde03d21e7d4459b09f0ac321c Mon Sep 17 00:00:00 2001 From: David Gasch Date: Fri, 13 May 2022 18:40:01 -0500 Subject: [PATCH 10/21] Removed jcenter and resolved build issues --- android/build.gradle | 5 ++++- android/gradle.properties | 15 +++++++++++++++ android/gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 android/gradle.properties diff --git a/android/build.gradle b/android/build.gradle index 09cf1bf..9d5931b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,12 +8,14 @@ buildscript { // module dependency in an application project. if (project == rootProject) { repositories { + mavenCentral() + maven { url "$rootDir/../node_modules/react-native/android" } google() - jcenter() } dependencies { classpath("com.android.tools.build:gradle:3.6.2") + } } } @@ -36,6 +38,7 @@ android { repositories { mavenCentral() + maven { url "$rootDir/../node_modules/react-native/android" } google() } diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..a6979eb --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,15 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e0c4de3..0ebb310 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 354570ac3092c93cdb9ef25f21d9a82ae7ad2534 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Wed, 18 May 2022 11:48:50 -0500 Subject: [PATCH 11/21] Updated the package-lock.json file to match the package.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index cbbf8e8..326f922 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-native-biometrics", - "version": "2.1.4", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { From 92613c86c41a94be83d1cec49cc4f7be912387ff Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Wed, 18 May 2022 11:52:26 -0500 Subject: [PATCH 12/21] Added allowDeviceCredentials as an optional option to isSensorAvailable. The new signature of the BiometricManager.canAuthenticate method requires a integer signaling which authenticators should be used when determining what sensors are available. The old method was deprecated in favor of being able to specify what class of biometric and device credentials are allowed. --- .../com/rnbiometrics/ReactNativeBiometrics.java | 5 +++-- index.ts | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java b/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java index e28eb7d..dca9ea5 100644 --- a/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java +++ b/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java @@ -48,12 +48,13 @@ public String getName() { } @ReactMethod - public void isSensorAvailable(Promise promise) { + public void isSensorAvailable(final ReadableMap params, final Promise promise) { try { if (isCurrentSDKMarshmallowOrLater()) { + boolean allowDeviceCredentials = params.getBoolean("allowDeviceCredentials"); ReactApplicationContext reactApplicationContext = getReactApplicationContext(); BiometricManager biometricManager = BiometricManager.from(reactApplicationContext); - int canAuthenticate = biometricManager.canAuthenticate(); + int canAuthenticate = biometricManager.canAuthenticate(getAllowedAuthenticators(allowDeviceCredentials)); if (canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS) { WritableMap resultMap = new WritableNativeMap(); diff --git a/index.ts b/index.ts index 2feacfe..9e48919 100644 --- a/index.ts +++ b/index.ts @@ -7,6 +7,10 @@ const { ReactNativeBiometrics: bridge } = NativeModules; */ export type BiometryType = 'TouchID' | 'FaceID' | 'Biometrics'; +interface IsSensorAvailableOptions { + allowDeviceCredentials?: boolean +} + interface IsSensorAvailableResult { available: boolean biometryType?: BiometryType @@ -28,6 +32,7 @@ interface DeleteKeysResult { interface CreateSignatureOptions { promptMessage: string payload: string + cancelButtonText?: string allowDeviceCredentials?: boolean } @@ -40,6 +45,7 @@ interface CreateSignatureResult { interface SimplePromptOptions { promptMessage: string fallbackPromptMessage?: string + cancelButtonText?: string allowDeviceCredentials?: boolean } @@ -66,8 +72,12 @@ module ReactNativeBiometrics { * Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID * @returns {Promise} Promise that resolves to an object with details about biometrics available */ - export function isSensorAvailable(): Promise { - return bridge.isSensorAvailable(); + export function isSensorAvailable(isSensorAvailableOptions?: IsSensorAvailableOptions): Promise { + const options = { + allowDeviceCredentials: isSensorAvailableOptions?.allowDeviceCredentials ?? false + } + + return bridge.isSensorAvailable(options); } /** * Creates a public private key pair,returns promise that resolves to From 7d03fc2ca558c0999f41923f809ec15d97f15589 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Wed, 18 May 2022 11:56:45 -0500 Subject: [PATCH 13/21] Added back cancelButtonText option. Added API version detection to the allowDeviceCredentials feature. This feature is only able to be combined with BIOMETRIC_STRONG (Class 3) devices on API versions greater or equal to API 30. Anything prior to API 30, does not support device credentials combined with BIOMETRIC_STRONG (Class 3) crypto-based authentication. --- README.md | 11 ++++++-- .../rnbiometrics/ReactNativeBiometrics.java | 26 ++++++++++++------- index.ts | 2 ++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0858e5d..cb20aeb 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,11 @@ if (biometryType === ReactNativeBiometrics.Biometrics) { Detects what type of biometric sensor is available. Returns a `Promise` that resolves to an object with details about biometrics availability +__Options Object__ +| Parameter | Type | Description | iOS | Android | +| --- | --- | --- | --- | --- | +| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. Note: This feature is not supported in Android versions prior to API 30. | ✖ | ✔ | + __Result Object__ | Property | Type | Description | @@ -222,7 +227,8 @@ __Options Object__ | --- | --- | --- | --- | --- | | promptMessage | string | Message that will be displayed in the fingerprint or face id prompt | ✔ | ✔ | | payload | string | String of data to be signed by the RSA signature | ✔ | ✔ | -| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. | ✖ | ✔ | +| cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | +| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. Note: This feature is not supported in Android versions prior to API 30. | ✖ | ✔ | __Result Object__ @@ -266,7 +272,8 @@ __Options Object__ | --- | --- | --- | --- | --- | | promptMessage | string | Message that will be displayed in the biometrics prompt | ✔ | ✔ | | fallbackPromptMessage | string | Message that will be shown when FaceID or TouchID has failed and a passcode has been set on the device. | ✔ | ✖ | -| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. | ✖ | ✔ | +| cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | +| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. Note: This feature is not supported in Android versions prior to API 30. | ✖ | ✔ | __Result Object__ diff --git a/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java b/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java index dca9ea5..58f110c 100644 --- a/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java +++ b/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java @@ -154,6 +154,7 @@ public void run() { try { String promptMessage = params.getString("promptMessage"); String payload = params.getString("payload"); + String cancelButtonText = params.getString("cancelButtonText"); boolean allowDeviceCredentials = params.getBoolean("allowDeviceCredentials"); Signature signature = Signature.getInstance("SHA256withRSA"); @@ -170,7 +171,7 @@ public void run() { Executor executor = Executors.newSingleThreadExecutor(); BiometricPrompt biometricPrompt = new BiometricPrompt(fragmentActivity, executor, authCallback); - biometricPrompt.authenticate(getPromptInfo(promptMessage, allowDeviceCredentials), cryptoObject); + biometricPrompt.authenticate(getPromptInfo(promptMessage, cancelButtonText, allowDeviceCredentials), cryptoObject); } catch (Exception e) { promise.reject("Error signing payload: " + e.getMessage(), "Error generating signature: " + e.getMessage()); } @@ -181,25 +182,29 @@ public void run() { } } - private PromptInfo getPromptInfo(String promptMessage, boolean allowDeviceCredentials) { - PromptInfo.Builder builder = new PromptInfo.Builder() - .setAllowedAuthenticators(getSupportedAuthenticators(allowDeviceCredentials)) - .setTitle(promptMessage); + private PromptInfo getPromptInfo(String promptMessage, String cancelButtonText, boolean allowDeviceCredentials) { + PromptInfo.Builder builder = new PromptInfo.Builder().setTitle(promptMessage); - if (allowDeviceCredentials == false) { - builder.setNegativeButtonText("Cancel"); + builder.setAllowedAuthenticators(getAllowedAuthenticators(allowDeviceCredentials)); + + if (allowDeviceCredentials == false || isCurrentSDK29OrEarlier()) { + builder.setNegativeButtonText(cancelButtonText); } return builder.build(); } - private int getSupportedAuthenticators(boolean allowDeviceCredentials) { - if (allowDeviceCredentials) { + private int getAllowedAuthenticators(boolean allowDeviceCredentials) { + if (allowDeviceCredentials && !isCurrentSDK29OrEarlier()) { return BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL; } return BiometricManager.Authenticators.BIOMETRIC_STRONG; } + private boolean isCurrentSDK29OrEarlier() { + return Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q; + } + @ReactMethod public void simplePrompt(final ReadableMap params, final Promise promise) { if (isCurrentSDKMarshmallowOrLater()) { @@ -209,6 +214,7 @@ public void simplePrompt(final ReadableMap params, final Promise promise) { public void run() { try { String promptMessage = params.getString("promptMessage"); + String cancelButtonText = params.getString("cancelButtonText"); boolean allowDeviceCredentials = params.getBoolean("allowDeviceCredentials"); AuthenticationCallback authCallback = new SimplePromptCallback(promise); @@ -216,7 +222,7 @@ public void run() { Executor executor = Executors.newSingleThreadExecutor(); BiometricPrompt biometricPrompt = new BiometricPrompt(fragmentActivity, executor, authCallback); - biometricPrompt.authenticate(getPromptInfo(promptMessage, allowDeviceCredentials)); + biometricPrompt.authenticate(getPromptInfo(promptMessage, cancelButtonText, allowDeviceCredentials)); } catch (Exception e) { promise.reject("Error displaying local biometric prompt: " + e.getMessage(), "Error displaying local biometric prompt: " + e.getMessage()); } diff --git a/index.ts b/index.ts index 9e48919..868f7b0 100644 --- a/index.ts +++ b/index.ts @@ -116,6 +116,7 @@ module ReactNativeBiometrics { * @returns {Promise} Promise that resolves to an object cryptographic signature details */ export function createSignature(createSignatureOptions: CreateSignatureOptions): Promise { + createSignatureOptions.cancelButtonText = createSignatureOptions.cancelButtonText ?? 'Cancel'; createSignatureOptions.allowDeviceCredentials = createSignatureOptions.allowDeviceCredentials ?? false return bridge.createSignature(createSignatureOptions); @@ -131,6 +132,7 @@ module ReactNativeBiometrics { * @returns {Promise} Promise that resolves an object with details about the biometrics result */ export function simplePrompt(simplePromptOptions: SimplePromptOptions): Promise { + simplePromptOptions.cancelButtonText = simplePromptOptions.cancelButtonText ?? 'Cancel'; simplePromptOptions.fallbackPromptMessage = simplePromptOptions.fallbackPromptMessage ?? 'Use Passcode'; simplePromptOptions.allowDeviceCredentials = simplePromptOptions.allowDeviceCredentials ?? false From d93b43c678ad5f7c2fc6f50682ff373a31b2613e Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Wed, 18 May 2022 19:36:33 -0500 Subject: [PATCH 14/21] Updated iOS to make passcode fallback optional. --- README.md | 11 ++++++++--- index.ts | 18 ++++++++++++++++-- ios/ReactNativeBiometrics.m | 35 ++++++++++++++++++++++++++++------- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index cb20aeb..1f21ea1 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Detects what type of biometric sensor is available. Returns a `Promise` that re __Options Object__ | Parameter | Type | Description | iOS | Android | | --- | --- | --- | --- | --- | -| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. Note: This feature is not supported in Android versions prior to API 30. | ✖ | ✔ | +| allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This feature is not supported in Android versions prior to API 30. | ✔ | ✔ | __Result Object__ @@ -142,6 +142,11 @@ ReactNativeBiometrics.isSensorAvailable() Generates a public private RSA 2048 key pair that will be stored in the device keystore. Returns a `Promise` that resolves to an object providing details about the keys. +__Options Object__ +| Parameter | Type | Description | iOS | Android | +| --- | --- | --- | --- | --- | +| allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This option is only needed to make iOS support for passcode fallback work. | ✔ | ✖ | + __Result Object__ | Property | Type | Description | @@ -228,7 +233,7 @@ __Options Object__ | promptMessage | string | Message that will be displayed in the fingerprint or face id prompt | ✔ | ✔ | | payload | string | String of data to be signed by the RSA signature | ✔ | ✔ | | cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | -| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. Note: This feature is not supported in Android versions prior to API 30. | ✖ | ✔ | +| allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This feature is not supported in Android versions prior to API 30. | ✔ | ✔ | __Result Object__ @@ -273,7 +278,7 @@ __Options Object__ | promptMessage | string | Message that will be displayed in the biometrics prompt | ✔ | ✔ | | fallbackPromptMessage | string | Message that will be shown when FaceID or TouchID has failed and a passcode has been set on the device. | ✔ | ✖ | | cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | -| allowDeviceCredentials | boolean | Boolean that provides the user the option to bypass using biometric information to retrieve the private key. Note: This feature is not supported in Android versions prior to API 30. | ✖ | ✔ | +| allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This feature is not supported in Android versions prior to API 30. | ✔ | ✔ | __Result Object__ diff --git a/index.ts b/index.ts index 868f7b0..6c0008c 100644 --- a/index.ts +++ b/index.ts @@ -17,6 +17,10 @@ interface IsSensorAvailableResult { error?: string } +interface CreateKeysOptions { + allowDeviceCredentials?: boolean +} + interface CreateKeysResult { publicKey: string } @@ -70,6 +74,8 @@ module ReactNativeBiometrics { /** * Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID + * @param {Object} isSensorAvailableOptions + * @param {boolean} isSensorAvailableOptions.allowDeviceCredentials * @returns {Promise} Promise that resolves to an object with details about biometrics available */ export function isSensorAvailable(isSensorAvailableOptions?: IsSensorAvailableOptions): Promise { @@ -82,10 +88,16 @@ module ReactNativeBiometrics { /** * Creates a public private key pair,returns promise that resolves to * an object with object.publicKey, which is the public key of the newly generated key pair + * @param {Object} createKeysOptions + * @param {boolean} createKeysOptions.allowDeviceCredentials * @returns {Promise} Promise that resolves to object with details about the newly generated public key */ - export function createKeys(): Promise { - return bridge.createKeys(); + export function createKeys(createKeysOptions?: CreateKeysOptions): Promise { + const options = { + allowDeviceCredentials: createKeysOptions?.allowDeviceCredentials ?? false + } + + return bridge.createKeys(options); } /** @@ -113,6 +125,7 @@ module ReactNativeBiometrics { * @param {Object} createSignatureOptions * @param {string} createSignatureOptions.promptMessage * @param {string} createSignatureOptions.payload + * @param {boolean} createSignatureOptions.allowDeviceCredentials * @returns {Promise} Promise that resolves to an object cryptographic signature details */ export function createSignature(createSignatureOptions: CreateSignatureOptions): Promise { @@ -129,6 +142,7 @@ module ReactNativeBiometrics { * @param {Object} simplePromptOptions * @param {string} simplePromptOptions.promptMessage * @param {string} simplePromptOptions.fallbackPromptMessage + * @param {boolean} simplePromptOptions.allowDeviceCredentials * @returns {Promise} Promise that resolves an object with details about the biometrics result */ export function simplePrompt(simplePromptOptions: SimplePromptOptions): Promise { diff --git a/ios/ReactNativeBiometrics.m b/ios/ReactNativeBiometrics.m index db0c24a..03ec388 100644 --- a/ios/ReactNativeBiometrics.m +++ b/ios/ReactNativeBiometrics.m @@ -13,11 +13,17 @@ @implementation ReactNativeBiometrics RCT_EXPORT_MODULE(ReactNativeBiometrics); -RCT_EXPORT_METHOD(isSensorAvailable:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) -{ +RCT_EXPORT_METHOD(isSensorAvailable: (NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { LAContext *context = [[LAContext alloc] init]; NSError *la_error = nil; - BOOL canEvaluatePolicy = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&la_error]; + BOOL allowDeviceCredentials = [RCTConvert BOOL:params[@"allowDeviceCredentials"]]; + LAPolicy laPolicy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + + if (allowDeviceCredentials == TRUE) { + laPolicy = LAPolicyDeviceOwnerAuthentication; + } + + BOOL canEvaluatePolicy = [context canEvaluatePolicy:laPolicy error:&la_error]; if (canEvaluatePolicy) { NSString *biometryType = [self getBiometryType:context]; @@ -38,13 +44,20 @@ @implementation ReactNativeBiometrics } } -RCT_EXPORT_METHOD(createKeys: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { +RCT_EXPORT_METHOD(createKeys: (NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ CFErrorRef error = NULL; + BOOL allowDeviceCredentials = [RCTConvert BOOL:params[@"allowDeviceCredentials"]]; + + SecAccessControlCreateFlags secCreateFlag = kSecAccessControlBiometryAny; + + if (allowDeviceCredentials == TRUE) { + secCreateFlag = kSecAccessControlUserPresence; + } SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, - kSecAccessControlUserPresence, &error); + secCreateFlag, &error); if (sacObject == NULL || error != NULL) { NSString *errorString = [NSString stringWithFormat:@"SecItemAdd can't create sacObject: %@", error]; reject(@"storage_error", errorString, nil); @@ -160,11 +173,19 @@ @implementation ReactNativeBiometrics dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *promptMessage = [RCTConvert NSString:params[@"promptMessage"]]; NSString *fallbackPromptMessage = [RCTConvert NSString:params[@"fallbackPromptMessage"]]; + BOOL allowDeviceCredentials = [RCTConvert BOOL:params[@"allowDeviceCredentials"]]; LAContext *context = [[LAContext alloc] init]; - context.localizedFallbackTitle = fallbackPromptMessage; + LAPolicy laPolicy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + + if (allowDeviceCredentials == TRUE) { + laPolicy = LAPolicyDeviceOwnerAuthentication; + context.localizedFallbackTitle = fallbackPromptMessage; + } else { + context.localizedFallbackTitle = @""; + } - [context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:promptMessage reply:^(BOOL success, NSError *biometricError) { + [context evaluatePolicy:laPolicy localizedReason:promptMessage reply:^(BOOL success, NSError *biometricError) { if (success) { NSDictionary *result = @{ @"success": @(YES) From a221a4cf59e566b2225269f2d52bfd3df42ef3aa Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Wed, 18 May 2022 20:27:10 -0500 Subject: [PATCH 15/21] Refactored the default export to be a class since allowing device credentials for authentication is a case where an instance variable is needed across all of the supporting methods. --- README.md | 55 ++++++------ index.ts | 257 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 158 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index 1f21ea1..40dc730 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,11 @@ A constant for the touch id sensor type, evaluates to `'TouchID'` __Example__ ```js -import ReactNativeBiometrics from 'react-native-biometrics' +import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics' -const { biometryType } = await ReactNativeBiometrics.isSensorAvailable() +const { biometryType } = await new ReactNativeBiometrics().isSensorAvailable() -if (biometryType === ReactNativeBiometrics.TouchID) { +if (biometryType === BiometryTypes.TouchID) { //do something fingerprint specific } ``` @@ -73,11 +73,11 @@ A constant for the face id sensor type, evaluates to `'FaceID'` __Example__ ```js -import ReactNativeBiometrics from 'react-native-biometrics' +import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics' -const { biometryType } = await ReactNativeBiometrics.isSensorAvailable() +const { biometryType } = await new ReactNativeBiometrics().isSensorAvailable() -if (biometryType === ReactNativeBiometrics.FaceID) { +if (biometryType === BiometryTypes.FaceID) { //do something face id specific } ``` @@ -89,26 +89,28 @@ A constant for generic Biometrics, evaluates to `'Biometrics'` __Example__ ```js -import ReactNativeBiometrics from 'react-native-biometrics' +import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics' -const { biometryType } = await ReactNativeBiometrics.isSensorAvailable() +const { biometryType } = await new ReactNativeBiometrics().isSensorAvailable() -if (biometryType === ReactNativeBiometrics.Biometrics) { +if (biometryType === BiometryTypes.Biometrics) { //do something face id specific } ``` -## Methods +## Class -### isSensorAvailable() - -Detects what type of biometric sensor is available. Returns a `Promise` that resolves to an object with details about biometrics availability +## Constructor __Options Object__ | Parameter | Type | Description | iOS | Android | | --- | --- | --- | --- | --- | | allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This feature is not supported in Android versions prior to API 30. | ✔ | ✔ | +### isSensorAvailable() + +Detects what type of biometric sensor is available. Returns a `Promise` that resolves to an object with details about biometrics availability + __Result Object__ | Property | Type | Description | @@ -120,17 +122,17 @@ __Result Object__ __Example__ ```js -import ReactNativeBiometrics from 'react-native-biometrics' +import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics' -ReactNativeBiometrics.isSensorAvailable() +new ReactNativeBiometrics().isSensorAvailable() .then((resultObject) => { const { available, biometryType } = resultObject - if (available && biometryType === ReactNativeBiometrics.TouchID) { + if (available && biometryType === BiometryTypes.TouchID) { console.log('TouchID is supported') - } else if (available && biometryType === ReactNativeBiometrics.FaceID) { + } else if (available && biometryType === BiometryTypes.FaceID) { console.log('FaceID is supported') - } else if (available && biometryType === ReactNativeBiometrics.Biometrics) { + } else if (available && biometryType === BiometryTypes.Biometrics) { console.log('Biometrics is supported') } else { console.log('Biometrics not supported') @@ -142,11 +144,6 @@ ReactNativeBiometrics.isSensorAvailable() Generates a public private RSA 2048 key pair that will be stored in the device keystore. Returns a `Promise` that resolves to an object providing details about the keys. -__Options Object__ -| Parameter | Type | Description | iOS | Android | -| --- | --- | --- | --- | --- | -| allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This option is only needed to make iOS support for passcode fallback work. | ✔ | ✖ | - __Result Object__ | Property | Type | Description | @@ -158,7 +155,7 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -ReactNativeBiometrics.createKeys() +new ReactNativeBiometrics().createKeys() .then((resultObject) => { const { publicKey } = resultObject console.log(publicKey) @@ -181,7 +178,7 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -ReactNativeBiometrics.biometricKeysExist() +new ReactNativeBiometrics().biometricKeysExist() .then((resultObject) => { const { keysExist } = resultObject @@ -208,7 +205,7 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -ReactNativeBiometrics.deleteKeys() +new ReactNativeBiometrics().deleteKeys() .then((resultObject) => { const { keysDeleted } = resultObject @@ -233,7 +230,6 @@ __Options Object__ | promptMessage | string | Message that will be displayed in the fingerprint or face id prompt | ✔ | ✔ | | payload | string | String of data to be signed by the RSA signature | ✔ | ✔ | | cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | -| allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This feature is not supported in Android versions prior to API 30. | ✔ | ✔ | __Result Object__ @@ -251,7 +247,7 @@ import ReactNativeBiometrics from 'react-native-biometrics' let epochTimeSeconds = Math.round((new Date()).getTime() / 1000).toString() let payload = epochTimeSeconds + 'some message' -ReactNativeBiometrics.createSignature({ +new ReactNativeBiometrics().createSignature({ promptMessage: 'Sign in', payload: payload }) @@ -278,7 +274,6 @@ __Options Object__ | promptMessage | string | Message that will be displayed in the biometrics prompt | ✔ | ✔ | | fallbackPromptMessage | string | Message that will be shown when FaceID or TouchID has failed and a passcode has been set on the device. | ✔ | ✖ | | cancelButtonText | string | Text to be displayed for the cancel button on biometric prompts, defaults to `Cancel` | ✖ | ✔ | -| allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This feature is not supported in Android versions prior to API 30. | ✔ | ✔ | __Result Object__ @@ -292,7 +287,7 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -ReactNativeBiometrics.simplePrompt({promptMessage: 'Confirm fingerprint'}) +new ReactNativeBiometrics().simplePrompt({promptMessage: 'Confirm fingerprint'}) .then((resultObject) => { const { success } = resultObject diff --git a/index.ts b/index.ts index 6c0008c..b6d5392 100644 --- a/index.ts +++ b/index.ts @@ -1,157 +1,166 @@ -import { NativeModules } from 'react-native'; +import { NativeModules } from 'react-native' -const { ReactNativeBiometrics: bridge } = NativeModules; +const { ReactNativeBiometrics: bridge } = NativeModules -/** +/** * Type alias for possible biometry types */ -export type BiometryType = 'TouchID' | 'FaceID' | 'Biometrics'; +export type BiometryType = 'TouchID' | 'FaceID' | 'Biometrics' -interface IsSensorAvailableOptions { - allowDeviceCredentials?: boolean +interface RNBiometricsOptions { + allowDeviceCredentials?: boolean } interface IsSensorAvailableResult { - available: boolean - biometryType?: BiometryType - error?: string -} - -interface CreateKeysOptions { - allowDeviceCredentials?: boolean + available: boolean + biometryType?: BiometryType + error?: string } interface CreateKeysResult { - publicKey: string + publicKey: string } interface BiometricKeysExistResult { - keysExist: boolean + keysExist: boolean } interface DeleteKeysResult { - keysDeleted: boolean + keysDeleted: boolean } interface CreateSignatureOptions { - promptMessage: string - payload: string - cancelButtonText?: string - allowDeviceCredentials?: boolean + promptMessage: string + payload: string + cancelButtonText?: string } interface CreateSignatureResult { - success: boolean - signature?: string - error?: string + success: boolean + signature?: string + error?: string } interface SimplePromptOptions { - promptMessage: string - fallbackPromptMessage?: string - cancelButtonText?: string - allowDeviceCredentials?: boolean + promptMessage: string + fallbackPromptMessage?: string + cancelButtonText?: string } interface SimplePromptResult { - success: boolean - error?: string + success: boolean + error?: string +} + +/** + * Enum for touch id sensor type + */ +export const TouchID = 'TouchID' +/** + * Enum for face id sensor type + */ +export const FaceID = 'FaceID' +/** + * Enum for generic biometrics (this is the only value available on android) + */ +export const Biometrics = 'Biometrics' + +export const BiometryTypes = { + TouchID, + FaceID, + Biometrics } -module ReactNativeBiometrics { - /** - * Enum for touch id sensor type - */ - export const TouchID = 'TouchID'; - /** - * Enum for face id sensor type - */ - export const FaceID = 'FaceID'; - /** - * Enum for generic biometrics (this is the only value available on android) - */ - export const Biometrics = 'Biometrics'; - - /** - * Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID - * @param {Object} isSensorAvailableOptions - * @param {boolean} isSensorAvailableOptions.allowDeviceCredentials - * @returns {Promise} Promise that resolves to an object with details about biometrics available - */ - export function isSensorAvailable(isSensorAvailableOptions?: IsSensorAvailableOptions): Promise { - const options = { - allowDeviceCredentials: isSensorAvailableOptions?.allowDeviceCredentials ?? false - } - - return bridge.isSensorAvailable(options); - } - /** - * Creates a public private key pair,returns promise that resolves to - * an object with object.publicKey, which is the public key of the newly generated key pair - * @param {Object} createKeysOptions - * @param {boolean} createKeysOptions.allowDeviceCredentials - * @returns {Promise} Promise that resolves to object with details about the newly generated public key - */ - export function createKeys(createKeysOptions?: CreateKeysOptions): Promise { - const options = { - allowDeviceCredentials: createKeysOptions?.allowDeviceCredentials ?? false - } - - return bridge.createKeys(options); - } - - /** - * Returns promise that resolves to an object with object.keysExists = true | false - * indicating if the keys were found to exist or not - * @returns {Promise} Promise that resolves to object with details aobut the existence of keys - */ - export function biometricKeysExist(): Promise { - return bridge.biometricKeysExist(); - } - - /** - * Returns promise that resolves to an object with true | false - * indicating if the keys were properly deleted - * @returns {Promise} Promise that resolves to an object with details about the deletion - */ - export function deleteKeys(): Promise { - return bridge.deleteKeys(); - } - - /** - * Prompts user with biometrics dialog using the passed in prompt message and - * returns promise that resolves to an object with object.signature, - * which is cryptographic signature of the payload - * @param {Object} createSignatureOptions - * @param {string} createSignatureOptions.promptMessage - * @param {string} createSignatureOptions.payload - * @param {boolean} createSignatureOptions.allowDeviceCredentials - * @returns {Promise} Promise that resolves to an object cryptographic signature details - */ - export function createSignature(createSignatureOptions: CreateSignatureOptions): Promise { - createSignatureOptions.cancelButtonText = createSignatureOptions.cancelButtonText ?? 'Cancel'; - createSignatureOptions.allowDeviceCredentials = createSignatureOptions.allowDeviceCredentials ?? false - - return bridge.createSignature(createSignatureOptions); - } - - /** - * Prompts user with biometrics dialog using the passed in prompt message and - * returns promise that resolves to an object with object.success = true if the user passes, - * object.success = false if the user cancels, and rejects if anything fails - * @param {Object} simplePromptOptions - * @param {string} simplePromptOptions.promptMessage - * @param {string} simplePromptOptions.fallbackPromptMessage - * @param {boolean} simplePromptOptions.allowDeviceCredentials - * @returns {Promise} Promise that resolves an object with details about the biometrics result - */ - export function simplePrompt(simplePromptOptions: SimplePromptOptions): Promise { - simplePromptOptions.cancelButtonText = simplePromptOptions.cancelButtonText ?? 'Cancel'; - simplePromptOptions.fallbackPromptMessage = simplePromptOptions.fallbackPromptMessage ?? 'Use Passcode'; - simplePromptOptions.allowDeviceCredentials = simplePromptOptions.allowDeviceCredentials ?? false - - return bridge.simplePrompt(simplePromptOptions); - } +class ReactNativeBiometrics { + allowDeviceCredentials = false + + /** + * Prompts user with biometrics dialog using the passed in prompt message and + * returns promise that resolves to an object with object.signature, + * which is cryptographic signature of the payload + * @param {Object} rnBiometricsOptions + * @param {boolean} rnBiometricsOptions.allowDeviceCredentials + */ + constructor(rnBiometricsOptions?: RNBiometricsOptions) { + const allowDeviceCredentials = rnBiometricsOptions?.allowDeviceCredentials ?? false + this.allowDeviceCredentials = allowDeviceCredentials + } + + /** + * Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID + * @returns {Promise} Promise that resolves to an object with details about biometrics available + */ + isSensorAvailable(): Promise { + return bridge.isSensorAvailable({ + allowDeviceCredentials: this.allowDeviceCredentials + }) + } + + /** + * Creates a public private key pair,returns promise that resolves to + * an object with object.publicKey, which is the public key of the newly generated key pair + * @returns {Promise} Promise that resolves to object with details about the newly generated public key + */ + createKeys(): Promise { + return bridge.createKeys({ + allowDeviceCredentials: this.allowDeviceCredentials + }) + } + + /** + * Returns promise that resolves to an object with object.keysExists = true | false + * indicating if the keys were found to exist or not + * @returns {Promise} Promise that resolves to object with details aobut the existence of keys + */ + biometricKeysExist(): Promise { + return bridge.biometricKeysExist() + } + + /** + * Returns promise that resolves to an object with true | false + * indicating if the keys were properly deleted + * @returns {Promise} Promise that resolves to an object with details about the deletion + */ + deleteKeys(): Promise { + return bridge.deleteKeys() + } + + /** + * Prompts user with biometrics dialog using the passed in prompt message and + * returns promise that resolves to an object with object.signature, + * which is cryptographic signature of the payload + * @param {Object} createSignatureOptions + * @param {string} createSignatureOptions.promptMessage + * @param {string} createSignatureOptions.payload + * @returns {Promise} Promise that resolves to an object cryptographic signature details + */ + createSignature(createSignatureOptions: CreateSignatureOptions): Promise { + createSignatureOptions.cancelButtonText = createSignatureOptions.cancelButtonText ?? 'Cancel' + + return bridge.createSignature({ + allowDeviceCredentials: this.allowDeviceCredentials, + ...createSignatureOptions + }) + } + + /** + * Prompts user with biometrics dialog using the passed in prompt message and + * returns promise that resolves to an object with object.success = true if the user passes, + * object.success = false if the user cancels, and rejects if anything fails + * @param {Object} simplePromptOptions + * @param {string} simplePromptOptions.promptMessage + * @param {string} simplePromptOptions.fallbackPromptMessage + * @returns {Promise} Promise that resolves an object with details about the biometrics result + */ + simplePrompt(simplePromptOptions: SimplePromptOptions): Promise { + simplePromptOptions.cancelButtonText = simplePromptOptions.cancelButtonText ?? 'Cancel' + simplePromptOptions.fallbackPromptMessage = simplePromptOptions.fallbackPromptMessage ?? 'Use Passcode' + + return bridge.simplePrompt({ + allowDeviceCredentials: this.allowDeviceCredentials, + ...simplePromptOptions + }) + } } -export default ReactNativeBiometrics; +export default ReactNativeBiometrics From 38fe1e0bcaf8a4220721e5931a9b8b5b22d2c08d Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Wed, 18 May 2022 20:30:05 -0500 Subject: [PATCH 16/21] Added details about changes implemented in this release thus far. --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1971785..8850572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog All notable changes to this project will be documented in this file. +## [2.2.0] - 2020-02-10 +## Changed +- iOS + + Added optional passcode fallback for iOS devices when FaceID or TouchID fails and the device has a passcode set. + + Added `fallbackPromptMessage` to `simplePrompt`. This controls the message that is shown when FaceID or TouchID has failed and the prompt falls back to the device passcode for authentication. +- Android + + Upgraded androidx.biomtric 1.1.0 + * Added `allowDeviceCredentials` option, for android devices, to `isSensorAvailable`, `createSignature` and `simplePrompt`. This option is only affects devices running API 30 or greater. Devices running API 29 or less cannot support device credentials when performing crypto based authentication. See https://developer.android.com/reference/androidx/biometric/BiometricPrompt.PromptInfo.Builder#setAllowedAuthenticators(int) + + Updated `build.gradle` file to avoid unnecessary downloads and potential conflicts when the library is included as a module dependency in an application project. + ## [2.1.4] - 2020-02-10 ## Changed - Removed duplicate onAuthenticationError call in android From 803e0a86079b6e5757c4d3141f3b49095d1c8d5d Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Thu, 19 May 2022 13:41:14 -0500 Subject: [PATCH 17/21] Refactored the API with the introduction of allowing device credentials to bypass biometric authentication. The default export is now a class that must be constructed before use. This ensures that all of the API calls are using the correct state for allowing device credentials. The old API can still be accessed with a named import `ReactNativeBiometricsLegacy` --- README.md | 13 +++- index.ts | 174 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 127 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 40dc730..6df61ce 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ This package is designed to make server authentication using biometrics easier. When a user enrolls in biometrics, a key pair is generated. The private key is stored securely on the device and the public key is sent to a server for registration. When the user wishes to authenticate, the user is prompted for biometrics, which unlocks the securely stored private key. Then a cryptographic signature is generated and sent to the server for verification. The server then verifies the signature. If the verification was successful, the server returns an appropriate response and authorizes the user. -## Constants +## Biometry Types ### TouchID (iOS only) @@ -105,7 +105,16 @@ if (biometryType === BiometryTypes.Biometrics) { __Options Object__ | Parameter | Type | Description | iOS | Android | | --- | --- | --- | --- | --- | -| allowDeviceCredentials | boolean | Boolean that will enable the user to opt to use their device passcode to bypass biometric authentication. Note: This feature is not supported in Android versions prior to API 30. | ✔ | ✔ | +| allowDeviceCredentials | boolean | Boolean that will enable the ability for the device passcode to be used instead of biometric information. On iOS, the prompt will only be shown after biometrics has failed twice. On Android, the prompt will be shown on the biometric prompt and does not require the user to attempt to use biometrics information first. Note: This feature is not supported on Android versions prior to API 30. | ✔ | ✔ | + +__Example__ + +```js +import ReactNativeBiometrics from 'react-native-biometrics' + +const biometrics = new ReactNativeBiometrics({ allowDeviceCredentials: true } + +``` ### isSensorAvailable() diff --git a/index.ts b/index.ts index b6d5392..9cd1236 100644 --- a/index.ts +++ b/index.ts @@ -52,48 +52,32 @@ interface SimplePromptResult { error?: string } -/** - * Enum for touch id sensor type - */ -export const TouchID = 'TouchID' -/** - * Enum for face id sensor type - */ -export const FaceID = 'FaceID' -/** - * Enum for generic biometrics (this is the only value available on android) - */ -export const Biometrics = 'Biometrics' - -export const BiometryTypes = { - TouchID, - FaceID, - Biometrics -} - -class ReactNativeBiometrics { - allowDeviceCredentials = false - +export module ReactNativeBiometricsLegacy { /** - * Prompts user with biometrics dialog using the passed in prompt message and - * returns promise that resolves to an object with object.signature, - * which is cryptographic signature of the payload - * @param {Object} rnBiometricsOptions - * @param {boolean} rnBiometricsOptions.allowDeviceCredentials + * Enum for touch id sensor type + */ + export const TouchID = 'TouchID' + /** + * Enum for face id sensor type */ - constructor(rnBiometricsOptions?: RNBiometricsOptions) { - const allowDeviceCredentials = rnBiometricsOptions?.allowDeviceCredentials ?? false - this.allowDeviceCredentials = allowDeviceCredentials + export const FaceID = 'FaceID' + /** + * Enum for generic biometrics (this is the only value available on android) + */ + export const Biometrics = 'Biometrics' + + export const BiometryTypes = { + TouchID, + FaceID, + Biometrics } /** * Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID * @returns {Promise} Promise that resolves to an object with details about biometrics available */ - isSensorAvailable(): Promise { - return bridge.isSensorAvailable({ - allowDeviceCredentials: this.allowDeviceCredentials - }) + export function isSensorAvailable(): Promise { + return new ReactNativeBiometrics().isSensorAvailable() } /** @@ -101,10 +85,8 @@ class ReactNativeBiometrics { * an object with object.publicKey, which is the public key of the newly generated key pair * @returns {Promise} Promise that resolves to object with details about the newly generated public key */ - createKeys(): Promise { - return bridge.createKeys({ - allowDeviceCredentials: this.allowDeviceCredentials - }) + export function createKeys(): Promise { + return new ReactNativeBiometrics().createKeys() } /** @@ -112,8 +94,8 @@ class ReactNativeBiometrics { * indicating if the keys were found to exist or not * @returns {Promise} Promise that resolves to object with details aobut the existence of keys */ - biometricKeysExist(): Promise { - return bridge.biometricKeysExist() + export function biometricKeysExist(): Promise { + return new ReactNativeBiometrics().biometricKeysExist() } /** @@ -121,8 +103,8 @@ class ReactNativeBiometrics { * indicating if the keys were properly deleted * @returns {Promise} Promise that resolves to an object with details about the deletion */ - deleteKeys(): Promise { - return bridge.deleteKeys() + export function deleteKeys(): Promise { + return new ReactNativeBiometrics().deleteKeys() } /** @@ -134,13 +116,8 @@ class ReactNativeBiometrics { * @param {string} createSignatureOptions.payload * @returns {Promise} Promise that resolves to an object cryptographic signature details */ - createSignature(createSignatureOptions: CreateSignatureOptions): Promise { - createSignatureOptions.cancelButtonText = createSignatureOptions.cancelButtonText ?? 'Cancel' - - return bridge.createSignature({ - allowDeviceCredentials: this.allowDeviceCredentials, - ...createSignatureOptions - }) + export function createSignature(createSignatureOptions: CreateSignatureOptions): Promise { + return new ReactNativeBiometrics().createSignature(createSignatureOptions) } /** @@ -152,15 +129,96 @@ class ReactNativeBiometrics { * @param {string} simplePromptOptions.fallbackPromptMessage * @returns {Promise} Promise that resolves an object with details about the biometrics result */ - simplePrompt(simplePromptOptions: SimplePromptOptions): Promise { - simplePromptOptions.cancelButtonText = simplePromptOptions.cancelButtonText ?? 'Cancel' - simplePromptOptions.fallbackPromptMessage = simplePromptOptions.fallbackPromptMessage ?? 'Use Passcode' - - return bridge.simplePrompt({ - allowDeviceCredentials: this.allowDeviceCredentials, - ...simplePromptOptions - }) + export function simplePrompt(simplePromptOptions: SimplePromptOptions): Promise { + return new ReactNativeBiometrics().simplePrompt(simplePromptOptions) } } -export default ReactNativeBiometrics +export default class ReactNativeBiometrics { + allowDeviceCredentials = false + + /** + * @param {Object} rnBiometricsOptions + * @param {boolean} rnBiometricsOptions.allowDeviceCredentials + */ + constructor(rnBiometricsOptions?: RNBiometricsOptions) { + const allowDeviceCredentials = rnBiometricsOptions?.allowDeviceCredentials ?? false + this.allowDeviceCredentials = allowDeviceCredentials + } + + /** + * Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID + * @returns {Promise} Promise that resolves to an object with details about biometrics available + */ + isSensorAvailable(): Promise { + return bridge.isSensorAvailable({ + allowDeviceCredentials: this.allowDeviceCredentials + }) + } + + /** + * Creates a public private key pair,returns promise that resolves to + * an object with object.publicKey, which is the public key of the newly generated key pair + * @returns {Promise} Promise that resolves to object with details about the newly generated public key + */ + createKeys(): Promise { + return bridge.createKeys({ + allowDeviceCredentials: this.allowDeviceCredentials + }) + } + + /** + * Returns promise that resolves to an object with object.keysExists = true | false + * indicating if the keys were found to exist or not + * @returns {Promise} Promise that resolves to object with details aobut the existence of keys + */ + biometricKeysExist(): Promise { + return bridge.biometricKeysExist() + } + + /** + * Returns promise that resolves to an object with true | false + * indicating if the keys were properly deleted + * @returns {Promise} Promise that resolves to an object with details about the deletion + */ + deleteKeys(): Promise { + return bridge.deleteKeys() + } + + /** + * Prompts user with biometrics dialog using the passed in prompt message and + * returns promise that resolves to an object with object.signature, + * which is cryptographic signature of the payload + * @param {Object} createSignatureOptions + * @param {string} createSignatureOptions.promptMessage + * @param {string} createSignatureOptions.payload + * @returns {Promise} Promise that resolves to an object cryptographic signature details + */ + createSignature(createSignatureOptions: CreateSignatureOptions): Promise { + createSignatureOptions.cancelButtonText = createSignatureOptions.cancelButtonText ?? 'Cancel' + + return bridge.createSignature({ + allowDeviceCredentials: this.allowDeviceCredentials, + ...createSignatureOptions + }) + } + + /** + * Prompts user with biometrics dialog using the passed in prompt message and + * returns promise that resolves to an object with object.success = true if the user passes, + * object.success = false if the user cancels, and rejects if anything fails + * @param {Object} simplePromptOptions + * @param {string} simplePromptOptions.promptMessage + * @param {string} simplePromptOptions.fallbackPromptMessage + * @returns {Promise} Promise that resolves an object with details about the biometrics result + */ + simplePrompt(simplePromptOptions: SimplePromptOptions): Promise { + simplePromptOptions.cancelButtonText = simplePromptOptions.cancelButtonText ?? 'Cancel' + simplePromptOptions.fallbackPromptMessage = simplePromptOptions.fallbackPromptMessage ?? 'Use Passcode' + + return bridge.simplePrompt({ + allowDeviceCredentials: this.allowDeviceCredentials, + ...simplePromptOptions + }) + } + } From 896e9fa8d1ce6a72be71f1c0dde64302024fa5c2 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Tue, 24 May 2022 09:52:44 -0500 Subject: [PATCH 18/21] Moved the named exports out of the old Typescript module so they can be properly imported using their names --- index.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/index.ts b/index.ts index 9cd1236..ef8f260 100644 --- a/index.ts +++ b/index.ts @@ -52,26 +52,26 @@ interface SimplePromptResult { error?: string } -export module ReactNativeBiometricsLegacy { - /** - * Enum for touch id sensor type - */ - export const TouchID = 'TouchID' - /** - * Enum for face id sensor type - */ - export const FaceID = 'FaceID' - /** - * Enum for generic biometrics (this is the only value available on android) - */ - export const Biometrics = 'Biometrics' +/** + * Enum for touch id sensor type + */ +export const TouchID = 'TouchID' +/** + * Enum for face id sensor type + */ +export const FaceID = 'FaceID' +/** + * Enum for generic biometrics (this is the only value available on android) + */ +export const Biometrics = 'Biometrics' - export const BiometryTypes = { - TouchID, - FaceID, - Biometrics - } +export const BiometryTypes = { + TouchID, + FaceID, + Biometrics +} +export module ReactNativeBiometricsLegacy { /** * Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID * @returns {Promise} Promise that resolves to an object with details about biometrics available From 0f062a5a0483d3d31276301ae79196cc931c1a29 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Tue, 31 May 2022 13:25:11 -0500 Subject: [PATCH 19/21] Updating documentation to more clearly distinguish the class pattern from it being a function based API --- README.md | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6df61ce..d13a5cc 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,9 @@ __Example__ ```js import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics' -const { biometryType } = await new ReactNativeBiometrics().isSensorAvailable() +const rnBiometrics = new ReactNativeBiometrics() + +const { biometryType } = await rnBiometrics.isSensorAvailable() if (biometryType === BiometryTypes.TouchID) { //do something fingerprint specific @@ -75,7 +77,9 @@ __Example__ ```js import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics' -const { biometryType } = await new ReactNativeBiometrics().isSensorAvailable() +const rnBiometrics = new ReactNativeBiometrics() + +const { biometryType } = await rnBiometrics.isSensorAvailable() if (biometryType === BiometryTypes.FaceID) { //do something face id specific @@ -91,7 +95,9 @@ __Example__ ```js import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics' -const { biometryType } = await new ReactNativeBiometrics().isSensorAvailable() +const rnBiometrics = new ReactNativeBiometrics() + +const { biometryType } = await rnBiometrics.isSensorAvailable() if (biometryType === BiometryTypes.Biometrics) { //do something face id specific @@ -112,7 +118,10 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -const biometrics = new ReactNativeBiometrics({ allowDeviceCredentials: true } +const rnBiometrics = new ReactNativeBiometrics({ allowDeviceCredentials: true }) + +// Perform operations as normal +// All prompts will allow for fallback to the device's credentials for authentication ``` @@ -133,7 +142,9 @@ __Example__ ```js import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics' -new ReactNativeBiometrics().isSensorAvailable() +const rnBiometrics = new ReactNativeBiometrics() + +rnBiometrics.isSensorAvailable() .then((resultObject) => { const { available, biometryType } = resultObject @@ -164,7 +175,9 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -new ReactNativeBiometrics().createKeys() +const rnBiometrics = new ReactNativeBiometrics() + +rnBiometrics.createKeys() .then((resultObject) => { const { publicKey } = resultObject console.log(publicKey) @@ -187,7 +200,8 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -new ReactNativeBiometrics().biometricKeysExist() +const rnBiometrics = new ReactNativeBiometrics() +rnBiometrics.biometricKeysExist() .then((resultObject) => { const { keysExist } = resultObject @@ -214,7 +228,9 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -new ReactNativeBiometrics().deleteKeys() +const rnBiometrics = new ReactNativeBiometrics() + +rnBiometrics.deleteKeys() .then((resultObject) => { const { keysDeleted } = resultObject @@ -256,7 +272,9 @@ import ReactNativeBiometrics from 'react-native-biometrics' let epochTimeSeconds = Math.round((new Date()).getTime() / 1000).toString() let payload = epochTimeSeconds + 'some message' -new ReactNativeBiometrics().createSignature({ +const rnBiometrics = new ReactNativeBiometrics() + +rnBiometrics.createSignature({ promptMessage: 'Sign in', payload: payload }) @@ -296,7 +314,9 @@ __Example__ ```js import ReactNativeBiometrics from 'react-native-biometrics' -new ReactNativeBiometrics().simplePrompt({promptMessage: 'Confirm fingerprint'}) +const rnBiometrics = new ReactNativeBiometrics() + +rnBiometrics.simplePrompt({promptMessage: 'Confirm fingerprint'}) .then((resultObject) => { const { success } = resultObject From 7168bbd1de3cb51f592a7d8af9afae543452ecd3 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Thu, 2 Jun 2022 11:01:12 -0500 Subject: [PATCH 20/21] Added missing parameter on Android class to handle the need for passing allowDeviceCredentials, which is needed for iOS --- .../src/main/java/com/rnbiometrics/ReactNativeBiometrics.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java b/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java index 58f110c..624ecd9 100644 --- a/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java +++ b/android/src/main/java/com/rnbiometrics/ReactNativeBiometrics.java @@ -91,7 +91,7 @@ public void isSensorAvailable(final ReadableMap params, final Promise promise) { } @ReactMethod - public void createKeys(Promise promise) { + public void createKeys(final ReadableMap params, Promise promise) { try { if (isCurrentSDKMarshmallowOrLater()) { deleteBiometricKey(); From b5a8b34f0eb33f99358b2274d8c44a8ed4a12d4c Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Thu, 2 Jun 2022 13:29:14 -0500 Subject: [PATCH 21/21] Updated changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8850572..41ae70f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,11 @@ All notable changes to this project will be documented in this file. ## [2.2.0] - 2020-02-10 ## Changed - iOS + + Fixed compatability issue with XCode 12 + Added optional passcode fallback for iOS devices when FaceID or TouchID fails and the device has a passcode set. + Added `fallbackPromptMessage` to `simplePrompt`. This controls the message that is shown when FaceID or TouchID has failed and the prompt falls back to the device passcode for authentication. - Android - + Upgraded androidx.biomtric 1.1.0 + + Upgraded androidx.biometric 1.1.0 * Added `allowDeviceCredentials` option, for android devices, to `isSensorAvailable`, `createSignature` and `simplePrompt`. This option is only affects devices running API 30 or greater. Devices running API 29 or less cannot support device credentials when performing crypto based authentication. See https://developer.android.com/reference/androidx/biometric/BiometricPrompt.PromptInfo.Builder#setAllowedAuthenticators(int) + Updated `build.gradle` file to avoid unnecessary downloads and potential conflicts when the library is included as a module dependency in an application project.