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

IOS: Additional option for keychain access group #276

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 142 additions & 121 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,92 +1,99 @@
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 RNBiometricsOptions {
allowDeviceCredentials?: boolean
allowDeviceCredentials?: boolean;
}

interface IsSensorAvailableResult {
available: boolean
biometryType?: BiometryType
error?: string
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
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
promptMessage: string;
fallbackPromptMessage?: string;
cancelButtonText?: string;
}

interface SimplePromptResult {
success: boolean
error?: string
success: boolean;
error?: string;
}

interface Options {
accessGroup?: string;
}

/**
* Enum for touch id sensor type
*/
export const TouchID = 'TouchID'
export const TouchID = "TouchID";
/**
* Enum for face id sensor type
*/
export const FaceID = 'FaceID'
export const FaceID = "FaceID";
/**
* Enum for generic biometrics (this is the only value available on android)
*/
export const Biometrics = 'Biometrics'
export const Biometrics = "Biometrics";

export const BiometryTypes = {
TouchID,
FaceID,
Biometrics
}
Biometrics,
};

export module ReactNativeBiometricsLegacy {
/**
* 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
*/
export function isSensorAvailable(): Promise<IsSensorAvailableResult> {
return new ReactNativeBiometrics().isSensorAvailable()
return new ReactNativeBiometrics().isSensorAvailable();
}

/**
* 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<Object>} Promise that resolves to object with details about the newly generated public key
*/
export function createKeys(): Promise<CreateKeysResult> {
return new ReactNativeBiometrics().createKeys()
export function createKeys(
createKeyOptions?: Options
): Promise<CreateKeysResult> {
const keyOptions = createKeyOptions || {};
return new ReactNativeBiometrics().createKeys(keyOptions);
}

/**
Expand All @@ -95,7 +102,7 @@ export module ReactNativeBiometricsLegacy {
* @returns {Promise<Object>} Promise that resolves to object with details about the existence of keys
*/
export function biometricKeysExist(): Promise<BiometricKeysExistResult> {
return new ReactNativeBiometrics().biometricKeysExist()
return new ReactNativeBiometrics().biometricKeysExist();
}

/**
Expand All @@ -104,7 +111,7 @@ export module ReactNativeBiometricsLegacy {
* @returns {Promise<Object>} Promise that resolves to an object with details about the deletion
*/
export function deleteKeys(): Promise<DeleteKeysResult> {
return new ReactNativeBiometrics().deleteKeys()
return new ReactNativeBiometrics().deleteKeys();
}

/**
Expand All @@ -116,8 +123,10 @@ export module ReactNativeBiometricsLegacy {
* @param {string} createSignatureOptions.payload
* @returns {Promise<Object>} Promise that resolves to an object cryptographic signature details
*/
export function createSignature(createSignatureOptions: CreateSignatureOptions): Promise<CreateSignatureResult> {
return new ReactNativeBiometrics().createSignature(createSignatureOptions)
export function createSignature(
createSignatureOptions: CreateSignatureOptions
): Promise<CreateSignatureResult> {
return new ReactNativeBiometrics().createSignature(createSignatureOptions);
}

/**
Expand All @@ -129,96 +138,108 @@ export module ReactNativeBiometricsLegacy {
* @param {string} simplePromptOptions.fallbackPromptMessage
* @returns {Promise<Object>} Promise that resolves an object with details about the biometrics result
*/
export function simplePrompt(simplePromptOptions: SimplePromptOptions): Promise<SimplePromptResult> {
return new ReactNativeBiometrics().simplePrompt(simplePromptOptions)
export function simplePrompt(
simplePromptOptions: SimplePromptOptions
): Promise<SimplePromptResult> {
return new ReactNativeBiometrics().simplePrompt(simplePromptOptions);
}
}

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<Object>} Promise that resolves to an object with details about biometrics available
*/
isSensorAvailable(): Promise<IsSensorAvailableResult> {
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<Object>} Promise that resolves to object with details about the newly generated public key
*/
createKeys(): Promise<CreateKeysResult> {
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<Object>} Promise that resolves to object with details aobut the existence of keys
*/
biometricKeysExist(): Promise<BiometricKeysExistResult> {
return bridge.biometricKeysExist()
}

/**
* Returns promise that resolves to an object with true | false
* indicating if the keys were properly deleted
* @returns {Promise<Object>} Promise that resolves to an object with details about the deletion
*/
deleteKeys(): Promise<DeleteKeysResult> {
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<Object>} Promise that resolves to an object cryptographic signature details
*/
createSignature(createSignatureOptions: CreateSignatureOptions): Promise<CreateSignatureResult> {
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<Object>} Promise that resolves an object with details about the biometrics result
*/
simplePrompt(simplePromptOptions: SimplePromptOptions): Promise<SimplePromptResult> {
simplePromptOptions.cancelButtonText = simplePromptOptions.cancelButtonText ?? 'Cancel'
simplePromptOptions.fallbackPromptMessage = simplePromptOptions.fallbackPromptMessage ?? 'Use Passcode'

return bridge.simplePrompt({
allowDeviceCredentials: this.allowDeviceCredentials,
...simplePromptOptions
})
}
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<Object>} Promise that resolves to an object with details about biometrics available
*/
isSensorAvailable(): Promise<IsSensorAvailableResult> {
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<Object>} Promise that resolves to object with details about the newly generated public key
*/
createKeys(createKeyOptions?: Options): Promise<CreateKeysResult> {
const keyOptions = createKeyOptions || {};
return bridge.createKeys({
allowDeviceCredentials: this.allowDeviceCredentials,
...keyOptions,
});
}

/**
* Returns promise that resolves to an object with object.keysExists = true | false
* indicating if the keys were found to exist or not
* @returns {Promise<Object>} Promise that resolves to object with details aobut the existence of keys
*/
biometricKeysExist(): Promise<BiometricKeysExistResult> {
return bridge.biometricKeysExist();
}

/**
* Returns promise that resolves to an object with true | false
* indicating if the keys were properly deleted
* @returns {Promise<Object>} Promise that resolves to an object with details about the deletion
*/
deleteKeys(): Promise<DeleteKeysResult> {
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<Object>} Promise that resolves to an object cryptographic signature details
*/
createSignature(
createSignatureOptions: CreateSignatureOptions
): Promise<CreateSignatureResult> {
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<Object>} Promise that resolves an object with details about the biometrics result
*/
simplePrompt(
simplePromptOptions: SimplePromptOptions
): Promise<SimplePromptResult> {
simplePromptOptions.cancelButtonText =
simplePromptOptions.cancelButtonText ?? "Cancel";
simplePromptOptions.fallbackPromptMessage =
simplePromptOptions.fallbackPromptMessage ?? "Use Passcode";

return bridge.simplePrompt({
allowDeviceCredentials: this.allowDeviceCredentials,
...simplePromptOptions,
});
}
}
10 changes: 9 additions & 1 deletion ios/ReactNativeBiometrics.m
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,17 @@ @implementation ReactNativeBiometrics
}
};

NSMutableDictionary *keyAttributesWithOptions = keyAttributes.mutableCopy;

NSString *accessGroup = [RCTConvert NSString:params[@"accessGroup"]];

if (accessGroup != nil) {
keyAttributesWithOptions[(id)kSecAttrAccessGroup] = accessGroup;
}

[self deleteBiometricKey];
NSError *gen_error = nil;
id privateKey = CFBridgingRelease(SecKeyCreateRandomKey((__bridge CFDictionaryRef)keyAttributes, (void *)&gen_error));
id privateKey = CFBridgingRelease(SecKeyCreateRandomKey((__bridge CFDictionaryRef)keyAttributesWithOptions, (void *)&gen_error));

if(privateKey != nil) {
id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));
Expand Down