BlueWallet/hooks/useBiometrics.ts

196 lines
6.0 KiB
TypeScript
Raw Normal View History

2024-06-04 03:27:21 +02:00
import { useState, useEffect, useCallback } from 'react';
2024-05-18 00:34:39 +02:00
import { Alert, Platform } from 'react-native';
import ReactNativeBiometrics, { BiometryTypes as RNBiometryTypes } from 'react-native-biometrics';
import RNSecureKeyStore, { ACCESSIBLE } from 'react-native-secure-key-store';
import loc from '../loc';
import * as NavigationService from '../NavigationService';
import presentAlert from '../components/Alert';
2024-05-31 19:18:01 +02:00
import { useStorage } from './context/useStorage';
2024-05-18 00:34:39 +02:00
const STORAGEKEY = 'Biometrics';
const rnBiometrics = new ReactNativeBiometrics({ allowDeviceCredentials: true });
const FaceID = 'Face ID';
const TouchID = 'Touch ID';
const Biometrics = 'Biometrics';
const clearKeychain = async () => {
try {
2024-06-04 03:27:21 +02:00
console.debug('Wiping keychain');
console.debug('Wiping key: data');
2024-05-18 00:34:39 +02:00
await RNSecureKeyStore.set('data', JSON.stringify({ data: { wallets: [] } }), {
accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
2024-06-04 03:27:21 +02:00
console.debug('Wiped key: data');
console.debug('Wiping key: data_encrypted');
2024-05-18 00:34:39 +02:00
await RNSecureKeyStore.set('data_encrypted', '', { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
2024-06-04 03:27:21 +02:00
console.debug('Wiped key: data_encrypted');
console.debug('Wiping key: STORAGEKEY');
2024-05-18 00:34:39 +02:00
await RNSecureKeyStore.set(STORAGEKEY, '', { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
2024-06-04 03:27:21 +02:00
console.debug('Wiped key: STORAGEKEY');
2024-05-18 00:34:39 +02:00
NavigationService.reset();
} catch (error: any) {
console.warn(error);
presentAlert({ message: error.message });
}
};
2024-06-04 03:27:21 +02:00
const unlockWithBiometrics = async () => {
2024-05-18 00:34:39 +02:00
try {
2024-06-04 03:27:21 +02:00
const { available } = await rnBiometrics.isSensorAvailable();
if (!available) {
return false;
2024-05-18 00:34:39 +02:00
}
2024-06-04 03:27:21 +02:00
return new Promise<boolean>(resolve => {
rnBiometrics
.simplePrompt({ promptMessage: loc.settings.biom_conf_identity })
.then((result: { success: any }) => {
if (result.success) {
resolve(true);
} else {
console.debug('Biometrics authentication failed');
resolve(false);
}
})
.catch((error: Error) => {
console.debug('Biometrics authentication error');
presentAlert({ message: error.message });
resolve(false);
});
});
} catch (e: Error | any) {
console.debug('Biometrics authentication error', e);
presentAlert({ message: e.message });
return false;
2024-05-18 00:34:39 +02:00
}
};
const showKeychainWipeAlert = () => {
if (Platform.OS === 'ios') {
Alert.alert(
loc.settings.encrypt_tstorage,
loc.settings.biom_10times,
[
{
text: loc._.cancel,
onPress: () => {
2024-06-04 03:27:21 +02:00
console.debug('Cancel Pressed');
2024-05-18 00:34:39 +02:00
},
style: 'cancel',
},
{
text: loc._.ok,
2024-06-04 03:27:21 +02:00
onPress: async () => {
const { available } = await rnBiometrics.isSensorAvailable();
if (!available) {
presentAlert({ message: loc.settings.biom_no_passcode });
return;
}
const isAuthenticated = await unlockWithBiometrics();
if (isAuthenticated) {
Alert.alert(
loc.settings.encrypt_tstorage,
loc.settings.biom_remove_decrypt,
[
{ text: loc._.cancel, style: 'cancel' },
{
text: loc._.ok,
style: 'destructive',
onPress: async () => await clearKeychain(),
},
],
{ cancelable: false },
);
}
},
2024-05-18 00:34:39 +02:00
style: 'default',
},
],
{ cancelable: false },
);
}
};
const useBiometrics = () => {
const { getItem, setItem } = useStorage();
const [biometricEnabled, setBiometricEnabled] = useState(false);
const [deviceBiometricType, setDeviceBiometricType] = useState<'TouchID' | 'FaceID' | 'Biometrics' | undefined>(undefined);
useEffect(() => {
const fetchBiometricEnabledStatus = async () => {
const enabled = await isBiometricUseEnabled();
setBiometricEnabled(enabled);
const biometricType = await type();
setDeviceBiometricType(biometricType);
};
fetchBiometricEnabledStatus();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
2024-06-04 03:27:21 +02:00
const isDeviceBiometricCapable = useCallback(async () => {
2024-05-18 00:34:39 +02:00
try {
const { available } = await rnBiometrics.isSensorAvailable();
return available;
} catch (e) {
2024-06-04 03:27:21 +02:00
console.debug('Biometrics isDeviceBiometricCapable failed');
console.debug(e);
2024-05-18 00:34:39 +02:00
setBiometricUseEnabled(false);
}
return false;
2024-06-04 03:27:21 +02:00
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
2024-05-18 00:34:39 +02:00
2024-06-04 03:27:21 +02:00
const type = useCallback(async () => {
2024-05-18 00:34:39 +02:00
try {
const { available, biometryType } = await rnBiometrics.isSensorAvailable();
if (!available) {
return undefined;
}
return biometryType;
} catch (e) {
2024-06-04 03:27:21 +02:00
console.debug('Biometrics biometricType failed');
console.debug(e);
2024-05-18 00:34:39 +02:00
return undefined;
}
2024-06-04 03:27:21 +02:00
}, []);
2024-05-18 00:34:39 +02:00
2024-06-04 03:27:21 +02:00
const isBiometricUseEnabled = useCallback(async () => {
2024-05-18 00:34:39 +02:00
try {
const enabledBiometrics = await getItem(STORAGEKEY);
return !!enabledBiometrics;
} catch (_) {}
return false;
2024-06-04 03:27:21 +02:00
}, [getItem]);
2024-05-18 00:34:39 +02:00
2024-06-04 03:27:21 +02:00
const isBiometricUseCapableAndEnabled = useCallback(async () => {
2024-05-18 00:34:39 +02:00
const isEnabled = await isBiometricUseEnabled();
const isCapable = await isDeviceBiometricCapable();
return isEnabled && isCapable;
2024-06-04 03:27:21 +02:00
}, [isBiometricUseEnabled, isDeviceBiometricCapable]);
2024-05-18 00:34:39 +02:00
2024-06-04 03:27:21 +02:00
const setBiometricUseEnabled = useCallback(
async (value: boolean) => {
await setItem(STORAGEKEY, value === true ? '1' : '');
setBiometricEnabled(value);
},
[setItem],
);
2024-05-18 00:34:39 +02:00
return {
isDeviceBiometricCapable,
deviceBiometricType,
isBiometricUseEnabled,
isBiometricUseCapableAndEnabled,
setBiometricUseEnabled,
clearKeychain,
biometricEnabled,
};
};
2024-06-04 03:27:21 +02:00
export { FaceID, TouchID, Biometrics, RNBiometryTypes as BiometricType, useBiometrics, showKeychainWipeAlert, unlockWithBiometrics };