Implement Secure device lock authentication (biometrics) with offline App PIN support
This commit is contained in:
70
src/screen/LockScreen.tsx
Normal file
70
src/screen/LockScreen.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
TextInput,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import { verifyPin } from '../auth/AppPinService';
|
||||
import { unlockWithDevice, unlockApp } from '../auth/AuthManager';
|
||||
|
||||
const LockScreen = ({ onUnlock }: any) => {
|
||||
const [pin, setPin] = useState('');
|
||||
|
||||
const handlePinUnlock = async () => {
|
||||
const valid = await verifyPin(pin);
|
||||
if (valid) {
|
||||
await unlockApp();
|
||||
onUnlock();
|
||||
} else {
|
||||
Alert.alert('Wrong PIN');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeviceUnlock = async () => {
|
||||
const res = await unlockWithDevice();
|
||||
if (res.success) {
|
||||
await unlockApp();
|
||||
onUnlock();
|
||||
} else {
|
||||
Alert.alert('Authentication Failed');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
|
||||
<Text style={{ fontSize: 20, marginBottom: 10 }}>
|
||||
Enter App PIN
|
||||
</Text>
|
||||
|
||||
<TextInput
|
||||
keyboardType="number-pad"
|
||||
secureTextEntry
|
||||
maxLength={4}
|
||||
value={pin}
|
||||
onChangeText={setPin}
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
padding: 12,
|
||||
borderRadius: 6,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
/>
|
||||
|
||||
<TouchableOpacity onPress={handlePinUnlock}>
|
||||
<Text style={{ color: 'blue', marginBottom: 20 }}>
|
||||
Unlock with PIN
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity onPress={handleDeviceUnlock}>
|
||||
<Text style={{ color: 'green' }}>
|
||||
Unlock with Device Lock
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default LockScreen;
|
||||
31
src/screen/SetPinScreen.tsx
Normal file
31
src/screen/SetPinScreen.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, TextInput, Text, TouchableOpacity } from 'react-native';
|
||||
import { savePin } from '../auth/AppPinService';
|
||||
|
||||
const SetPinScreen = ({ onDone }: any) => {
|
||||
const [pin, setPin] = useState('');
|
||||
|
||||
const save = async () => {
|
||||
await savePin(pin);
|
||||
onDone();
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
|
||||
<Text>Create App PIN</Text>
|
||||
<TextInput
|
||||
secureTextEntry
|
||||
keyboardType="number-pad"
|
||||
maxLength={4}
|
||||
value={pin}
|
||||
onChangeText={setPin}
|
||||
style={{ borderWidth: 1, padding: 12, marginVertical: 10 }}
|
||||
/>
|
||||
<TouchableOpacity onPress={save}>
|
||||
<Text style={{ color: 'blue' }}>Save PIN</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SetPinScreen;
|
||||
20
src/screen/authorised/AppPinService.ts
Normal file
20
src/screen/authorised/AppPinService.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
|
||||
const PIN_KEY = 'APP_PIN';
|
||||
|
||||
export const savePin = async (pin: string) => {
|
||||
await Keychain.setGenericPassword(PIN_KEY, pin, {
|
||||
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyPin = async (inputPin: string) => {
|
||||
const creds = await Keychain.getGenericPassword();
|
||||
if (!creds) return false;
|
||||
return creds.password === inputPin;
|
||||
};
|
||||
|
||||
export const isPinSet = async () => {
|
||||
const creds = await Keychain.getGenericPassword();
|
||||
return !!creds;
|
||||
};
|
||||
21
src/screen/authorised/AuthManager.ts
Normal file
21
src/screen/authorised/AuthManager.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { deviceAuthenticate } from './DeviceAuth';
|
||||
|
||||
const LOCK_KEY = 'APP_LOCKED';
|
||||
|
||||
export const lockApp = async () => {
|
||||
await AsyncStorage.setItem(LOCK_KEY, 'true');
|
||||
};
|
||||
|
||||
export const unlockApp = async () => {
|
||||
await AsyncStorage.removeItem(LOCK_KEY);
|
||||
};
|
||||
|
||||
export const isAppLocked = async () => {
|
||||
const v = await AsyncStorage.getItem(LOCK_KEY);
|
||||
return v === 'true';
|
||||
};
|
||||
|
||||
export const unlockWithDevice = async () => {
|
||||
return await deviceAuthenticate();
|
||||
};
|
||||
Reference in New Issue
Block a user