diff --git a/android/src/main/java/com/reactnativesimplebiometrics/SimpleBiometricsModule.java b/android/src/main/java/com/reactnativesimplebiometrics/SimpleBiometricsModule.java index d4c53a4..64fff5f 100644 --- a/android/src/main/java/com/reactnativesimplebiometrics/SimpleBiometricsModule.java +++ b/android/src/main/java/com/reactnativesimplebiometrics/SimpleBiometricsModule.java @@ -1,6 +1,7 @@ package com.reactnativesimplebiometrics; import android.app.Activity; +import android.content.pm.PackageManager; import androidx.annotation.NonNull; import java.util.concurrent.Executor; @@ -16,8 +17,6 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentActivity; - - @ReactModule(name = SimpleBiometricsModule.NAME) public class SimpleBiometricsModule extends ReactContextBaseJavaModule { public static final String NAME = "SimpleBiometrics"; @@ -96,4 +95,22 @@ public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationRes ); } + + @ReactMethod + public void getBiometryType(Promise promise) { + try { + ReactApplicationContext context = getReactApplicationContext(); + PackageManager pm = context.getPackageManager(); + + if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + promise.resolve("Fingerprint"); + } else if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + promise.resolve("Face"); + } else { + promise.resolve("None"); + } + } catch (Exception e) { + promise.resolve("Unknown"); + } + } } diff --git a/example/App.tsx b/example/App.tsx index 2743461..ed9fab7 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -10,12 +10,21 @@ import RNBiometrics from 'react-native-simple-biometrics'; const App = () => { const [canAuth, setCanAuth] = useState(false); + const [type, setType] = useState(''); const [authenticated, setAuthenticated] = useState(false); useEffect(() => { RNBiometrics.canAuthenticate().then(setCanAuth); }, []); + useEffect(() => { + if (!canAuth) { + setType('Unknown'); + return; + } + RNBiometrics.getBiometryType().then(setType); + }, [canAuth]); + const authenticate = useCallback(async () => { try { const success = await RNBiometrics.requestBioAuth( @@ -40,7 +49,7 @@ const App = () => { {authenticated ? '🔓' : '🔒'} - {authenticated ? '$1,000,000' : '(tap to unlock)'} + {authenticated ? '$1,000,000' : `(tap to unlock using ${type})`} ) : ( diff --git a/ios/RNSimpleBiometricsSpec.h b/ios/RNSimpleBiometricsSpec.h new file mode 100644 index 0000000..8760e14 --- /dev/null +++ b/ios/RNSimpleBiometricsSpec.h @@ -0,0 +1,20 @@ +#import +#import +#import +#import + +namespace facebook { +namespace react { + + class JSI_EXPORT NativeSimpleBiometricsSpecJSI : public ObjCTurboModule { + public: + NativeSimpleBiometricsSpecJSI(const ObjCTurboModule::InitParams ¶ms); + + // Declare the methods your module exports + void canAuthenticate(jsi::Runtime &rt, std::function &&resolve, std::function &&reject); + void requestBioAuth(jsi::Runtime &rt, const std::string &title, const std::string &subtitle, std::function &&resolve, std::function &&reject); + void getBiometryType(jsi::Runtime &rt, std::function &&resolve, std::function &&reject); + }; + +} +} diff --git a/ios/SimpleBiometrics.mm b/ios/SimpleBiometrics.mm index 1bad425..e9e8812 100644 --- a/ios/SimpleBiometrics.mm +++ b/ios/SimpleBiometrics.mm @@ -56,6 +56,34 @@ @implementation SimpleBiometrics } +RCT_REMAP_METHOD(getBiometryType, + getBiometryTypeWithResolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + LAContext *context = [[LAContext alloc] init]; + NSError *la_error = nil; + + // Check if the device can evaluate a biometric policy + if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&la_error]) { + if (@available(iOS 11.0, *)) { + // iOS 11+ supports biometryType property + if (context.biometryType == LABiometryTypeFaceID) { + resolve(@"FaceID"); + } else if (context.biometryType == LABiometryTypeTouchID) { + resolve(@"TouchID"); + } else { + resolve(@"None"); + } + } else { + // Fallback for iOS versions earlier than 11.0 + resolve(@"Unknown"); + } + } else { + // Handle the error case where biometric authentication is not available + resolve(@"None"); + } +} + // Don't compile this code when we build for the old architecture. #ifdef RCT_NEW_ARCH_ENABLED - (std::shared_ptr)getTurboModule: diff --git a/package.json b/package.json index ff04659..877c7ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-simple-biometrics", - "version": "1.5.2", + "version": "1.8.0", "description": "test", "main": "lib/commonjs/index", "module": "lib/module/index", @@ -35,13 +35,13 @@ "ios", "android" ], - "repository": "https://github.com/smallcase/react-native-simple-biometrics", + "repository": "https://github.com/kierancrown/react-native-simple-biometrics", "author": "smallcase (https://github.com/smallcase)", "license": "MIT", "bugs": { - "url": "https://github.com/smallcase/react-native-simple-biometrics/issues" + "url": "https://github.com/kierancrown/react-native-simple-biometrics/issues" }, - "homepage": "https://github.com/smallcase/react-native-simple-biometrics#readme", + "homepage": "https://github.com/kierancrown/react-native-simple-biometrics#readme", "publishConfig": { "registry": "https://registry.npmjs.org/" }, @@ -49,11 +49,11 @@ "@arkweid/lefthook": "^0.7.7", "@babel/eslint-parser": "^7.18.2", "@commitlint/config-conventional": "^17.0.2", - "@react-native-community/eslint-config": "^3.0.2", + "@react-native-community/eslint-config": "^3.2.0", "@release-it/conventional-changelog": "^5.0.0", "@types/jest": "^28.1.2", "@types/react": "~17.0.21", - "@types/react-native": "0.68.0", + "@types/react-native": "^0.73.0", "commitlint": "^17.0.2", "eslint": "^8.4.1", "eslint-config-prettier": "^8.5.0", @@ -61,8 +61,8 @@ "jest": "^28.1.1", "pod-install": "^0.1.0", "prettier": "^2.0.5", - "react": "17.0.2", - "react-native": "0.68.2", + "react": "18.3.1", + "react-native": "^0.76.0", "react-native-builder-bob": "^0.18.3", "release-it": "^15.0.0", "typescript": "^4.5.2" diff --git a/react-native-simple-biometrics.podspec b/react-native-simple-biometrics.podspec index eb3e077..19ee038 100644 --- a/react-native-simple-biometrics.podspec +++ b/react-native-simple-biometrics.podspec @@ -1,7 +1,7 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) -folly_version = '2021.06.28.00-v2' +folly_version = '2024.01.01.00' folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' Pod::Spec.new do |s| @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.authors = package["author"] s.platforms = { :ios => "10.0" } - s.source = { :git => "https://github.com/smallcase/react-native-simple-biometrics.git", :tag => "#{s.version}" } + s.source = { :git => "https://github.com/kierancrown/react-native-simple-biometrics.git", :tag => "#{s.version}" } s.source_files = "ios/**/*.{h,m,mm}" diff --git a/src/index.tsx b/src/index.tsx index 1f688e0..4c77e4a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -32,9 +32,19 @@ const requestBioAuth = ( return RNBiometricsNative.requestBioAuth(promptTitle, promptMessage); }; +/** + * Get the type of biometric authentication available on the device + */ +const getBiometryType = (): Promise< + 'Fingerprint' | 'Face' | 'None' | 'Unknown' +> => { + return RNBiometricsNative.getBiometryType(); +}; + const RNBiometrics = { requestBioAuth, canAuthenticate, + getBiometryType, }; export default RNBiometrics;