Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: adds Credentials to BiometryType #231

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.rnbiometrics;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
Expand Down Expand Up @@ -54,13 +56,46 @@ public void isSensorAvailable(final ReadableMap params, final Promise promise) {
boolean allowDeviceCredentials = params.getBoolean("allowDeviceCredentials");
ReactApplicationContext reactApplicationContext = getReactApplicationContext();
BiometricManager biometricManager = BiometricManager.from(reactApplicationContext);
int canAuthenticate = biometricManager.canAuthenticate(getAllowedAuthenticators(allowDeviceCredentials));
PackageManager packageManager = reactApplicationContext.getPackageManager();
int canAuthenticate = biometricManager.canAuthenticate(getAllowedAuthenticators(false));

if (canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS) {
WritableMap resultMap = new WritableNativeMap();
resultMap.putBoolean("available", true);
resultMap.putString("biometryType", "Biometrics");

if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT) == true) {
resultMap.putString("biometryType", "Fingerprint");
}


promise.resolve(resultMap);
} else if (allowDeviceCredentials) {
int canAuthenticateCredentials = biometricManager.canAuthenticate(getAllowedAuthenticators(allowDeviceCredentials));
jensdev marked this conversation as resolved.
Show resolved Hide resolved

if (canAuthenticateCredentials == BiometricManager.BIOMETRIC_SUCCESS) {
WritableMap resultMap = new WritableNativeMap();
resultMap.putBoolean("available", true);
resultMap.putString("biometryType", "Credentials");
promise.resolve(resultMap);
} else {
WritableMap resultMap = new WritableNativeMap();
resultMap.putBoolean("available", false);

switch (canAuthenticateCredentials) {
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
resultMap.putString("error", "BIOMETRIC_ERROR_NO_HARDWARE");
break;
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
resultMap.putString("error", "BIOMETRIC_ERROR_HW_UNAVAILABLE");
break;
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
resultMap.putString("error", "BIOMETRIC_ERROR_NONE_ENROLLED");
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is a duplicate of the else block starting on line 101. You should extract these two pieces and re-use them instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've extracted this bit to a seperate function.


promise.resolve(resultMap);
}
} else {
WritableMap resultMap = new WritableNativeMap();
resultMap.putBoolean("available", false);
Expand Down Expand Up @@ -125,6 +160,10 @@ private boolean isCurrentSDKMarshmallowOrLater() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}

private boolean isCurrentSDKSnowConeOrLater() {
jensdev marked this conversation as resolved.
Show resolved Hide resolved
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
}

@ReactMethod
public void deleteKeys(Promise promise) {
if (doesBiometricKeyExist()) {
Expand Down
28 changes: 20 additions & 8 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ const { ReactNativeBiometrics: bridge } = NativeModules
/**
* Type alias for possible biometry types
*/
export type BiometryType = 'TouchID' | 'FaceID' | 'Biometrics'
export type BiometryTypeIOS = 'TouchID' | 'FaceID'
export type BiometryTypeAndroid ='Fingerprint' | 'Biometrics' | 'Credentials'
export type BiometryType = BiometryTypeIOS | BiometryTypeAndroid

interface RNBiometricsOptions {
allowDeviceCredentials?: boolean
}

interface isSensorAvailable {
allowDeviceCredentials?: boolean
}

interface IsSensorAvailableResult {
available: boolean
biometryType?: BiometryType
Expand Down Expand Up @@ -45,6 +51,7 @@ interface SimplePromptOptions {
promptMessage: string
fallbackPromptMessage?: string
cancelButtonText?: string
allowDeviceCredentials?: boolean
}

interface SimplePromptResult {
Expand All @@ -64,20 +71,24 @@ export const FaceID = 'FaceID'
* Enum for generic biometrics (this is the only value available on android)
*/
export const Biometrics = 'Biometrics'
export const Fingerprint = 'Fingerprint'
export const Credentials = 'Credentials'

export const BiometryTypes = {
TouchID,
FaceID,
Biometrics
Biometrics,
Fingerprint,
Credentials
}

export module ReactNativeBiometricsLegacy {
/**
* Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID
* Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID | Credentials
* @returns {Promise<Object>} Promise that resolves to an object with details about biometrics available
*/
export function isSensorAvailable(): Promise<IsSensorAvailableResult> {
return new ReactNativeBiometrics().isSensorAvailable()
export function isSensorAvailable(params: isSensorAvailable): Promise<IsSensorAvailableResult> {
return new ReactNativeBiometrics().isSensorAvailable(params)
}

/**
Expand Down Expand Up @@ -127,6 +138,7 @@ export module ReactNativeBiometricsLegacy {
* @param {Object} simplePromptOptions
* @param {string} simplePromptOptions.promptMessage
* @param {string} simplePromptOptions.fallbackPromptMessage
* @param {boolean} simplePromptOptions.allowDeviceCredentials
* @returns {Promise<Object>} Promise that resolves an object with details about the biometrics result
*/
export function simplePrompt(simplePromptOptions: SimplePromptOptions): Promise<SimplePromptResult> {
Expand All @@ -150,9 +162,9 @@ export default class ReactNativeBiometrics {
* Returns promise that resolves to an object with object.biometryType = Biometrics | TouchID | FaceID
* @returns {Promise<Object>} Promise that resolves to an object with details about biometrics available
*/
isSensorAvailable(): Promise<IsSensorAvailableResult> {
isSensorAvailable(params?: isSensorAvailable): Promise<IsSensorAvailableResult> {
return bridge.isSensorAvailable({
allowDeviceCredentials: this.allowDeviceCredentials
allowDeviceCredentials: params?.allowDeviceCredentials ?? this.allowDeviceCredentials
})
}

Expand Down Expand Up @@ -217,7 +229,7 @@ export default class ReactNativeBiometrics {
simplePromptOptions.fallbackPromptMessage = simplePromptOptions.fallbackPromptMessage ?? 'Use Passcode'

return bridge.simplePrompt({
allowDeviceCredentials: this.allowDeviceCredentials,
allowDeviceCredentials: simplePromptOptions.allowDeviceCredentials ?? this.allowDeviceCredentials,
...simplePromptOptions
})
}
Expand Down
40 changes: 27 additions & 13 deletions ios/ReactNativeBiometrics.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,10 @@ @implementation ReactNativeBiometrics
RCT_EXPORT_METHOD(isSensorAvailable: (NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
LAContext *context = [[LAContext alloc] init];
NSError *la_error = nil;
NSError *la_errorWithCredentials = nil;
BOOL allowDeviceCredentials = [RCTConvert BOOL:params[@"allowDeviceCredentials"]];
LAPolicy laPolicy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;

if (allowDeviceCredentials == TRUE) {
laPolicy = LAPolicyDeviceOwnerAuthentication;
}

BOOL canEvaluatePolicy = [context canEvaluatePolicy:laPolicy error:&la_error];
BOOL canEvaluatePolicy = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&la_error];

if (canEvaluatePolicy) {
NSString *biometryType = [self getBiometryType:context];
Expand All @@ -33,14 +29,32 @@ @implementation ReactNativeBiometrics
};

resolve(result);
} else {
NSString *errorMessage = [NSString stringWithFormat:@"%@", la_error];
NSDictionary *result = @{
@"available": @(NO),
@"error": errorMessage
};
} else if (allowDeviceCredentials == TRUE) {
BOOL canEvaluatePolicyWithCredentials = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&la_errorWithCredentials];
if(canEvaluatePolicyWithCredentials == TRUE) {
NSDictionary *result = @{
@"available": @(YES),
@"biometryType": @"Credentials"
};

resolve(result);
resolve(result);
} else {
NSString *errorMessage = [NSString stringWithFormat:@"%@", la_errorWithCredentials];
NSDictionary *result = @{
@"available": @(NO),
@"error": errorMessage
};

resolve(result);
}
} else {
NSString *errorMessage = [NSString stringWithFormat:@"%@", la_error];
NSDictionary *result = @{
@"available": @(NO),
@"error": errorMessage
};

resolve(result);
}
}

Expand Down