192 lines
5.8 KiB
TypeScript
192 lines
5.8 KiB
TypeScript
/*
|
|
* File: SetupBiometricScreen.tsx
|
|
* Description: Screen for setting up and authenticating with device biometrics or PIN.
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
TouchableOpacity,
|
|
Alert,
|
|
StyleSheet,
|
|
SafeAreaView,
|
|
} from 'react-native';
|
|
import ReactNativeBiometrics, {
|
|
BiometryTypes,
|
|
BiometryType,
|
|
} from 'react-native-biometrics';
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
import { Colors, Spacing, Typography, Shadows } from '../../../../shared/src/theme';
|
|
import { BASE_URL } from '../../../constants/URLS';
|
|
|
|
const PAYLOAD = 'neo-scan-secure-payload'; // A static payload for signature
|
|
|
|
/**
|
|
* SetupBiometricScreen - Manages user authentication using the device's
|
|
* built-in biometrics or PIN/password.
|
|
*/
|
|
const SetupBiometricScreen: React.FC = () => {
|
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
const [biometryType, setBiometryType] = useState<BiometryType | null>(null);
|
|
const [isAvailable, setIsAvailable] = useState(false);
|
|
const rnBiometrics = new ReactNativeBiometrics({ allowDeviceCredentials: true });
|
|
console.log('base url',BASE_URL)
|
|
useEffect(() => {
|
|
checkBiometricAvailability();
|
|
checkAuthStatus();
|
|
}, []);
|
|
|
|
const checkBiometricAvailability = async () => {
|
|
try {
|
|
const { available, biometryType: bioType } = await rnBiometrics.isSensorAvailable();
|
|
setIsAvailable(available);
|
|
setBiometryType(bioType || null);
|
|
} catch (error) {
|
|
console.error('Biometric check failed:', error);
|
|
Alert.alert('Error', 'Could not check for biometric features.');
|
|
}
|
|
};
|
|
|
|
const checkAuthStatus = async () => {
|
|
try {
|
|
const authStatus = await AsyncStorage.getItem('isAuthenticated');
|
|
setIsAuthenticated(authStatus === 'true');
|
|
} catch (error) {
|
|
console.error('Auth status check failed:', error);
|
|
}
|
|
};
|
|
|
|
const handleSuccessfulAuth = async () => {
|
|
setIsAuthenticated(true);
|
|
await AsyncStorage.setItem('isAuthenticated', 'true');
|
|
Alert.alert('Success', 'Authentication successful!');
|
|
};
|
|
|
|
const authenticateUser = async () => {
|
|
if (!isAvailable) {
|
|
Alert.alert('Not Available', 'Biometrics are not supported on this device.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Check if keys exist. If not, create them.
|
|
const { keysExist } = await rnBiometrics.biometricKeysExist();
|
|
if (!keysExist) {
|
|
await rnBiometrics.createKeys();
|
|
}
|
|
|
|
// Create a signature to authenticate. This will fallback to device PIN.
|
|
const { success, signature } = await rnBiometrics.createSignature({
|
|
promptMessage: 'Authenticate',
|
|
payload: PAYLOAD,
|
|
});
|
|
|
|
if (success && signature) {
|
|
// In a real app, you would verify the signature on your server.
|
|
// For this example, we'll assume it's valid.
|
|
await handleSuccessfulAuth();
|
|
} else {
|
|
Alert.alert('Authentication Failed', 'You could not be authenticated.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Authentication failed:', error);
|
|
Alert.alert('Error', 'An unexpected error occurred during authentication.');
|
|
}
|
|
};
|
|
|
|
const getBiometricTypeText = () => {
|
|
switch (biometryType) {
|
|
case BiometryTypes.TouchID: return 'Touch ID';
|
|
case BiometryTypes.FaceID: return 'Face ID';
|
|
case BiometryTypes.Biometrics: return 'Fingerprint';
|
|
default: return 'Device Credentials';
|
|
}
|
|
};
|
|
|
|
const logout = async () => {
|
|
try {
|
|
setIsAuthenticated(false);
|
|
await AsyncStorage.setItem('isAuthenticated', 'false');
|
|
} catch (error) {
|
|
console.error('Logout failed:', error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<SafeAreaView style={styles.container}>
|
|
{isAuthenticated ? (
|
|
<View style={styles.content}>
|
|
<Text style={styles.title}>Welcome!</Text>
|
|
<Text style={styles.subtitle}>You are successfully authenticated.</Text>
|
|
<TouchableOpacity style={styles.button} onPress={logout}>
|
|
<Text style={styles.buttonText}>Logout</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
) : (
|
|
<View style={styles.content}>
|
|
<Text style={styles.title}>Authentication Required</Text>
|
|
{isAvailable ? (
|
|
<Text style={styles.subtitle}>
|
|
Use {getBiometricTypeText()} or your device PIN to continue.
|
|
</Text>
|
|
) : (
|
|
<Text style={styles.subtitle}>
|
|
Biometrics not available. Please use your device PIN to continue.
|
|
</Text>
|
|
)}
|
|
<TouchableOpacity style={styles.button} onPress={authenticateUser}>
|
|
<Text style={styles.buttonText}>Authenticate</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
)}
|
|
</SafeAreaView>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: Colors.backgroundAlt,
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding: Spacing.lg,
|
|
},
|
|
title: {
|
|
fontFamily: Typography.fontFamily.bold,
|
|
fontSize: Typography.fontSize.title,
|
|
color: Colors.textPrimary,
|
|
marginBottom: Spacing.md,
|
|
textAlign: 'center',
|
|
},
|
|
subtitle: {
|
|
fontFamily: Typography.fontFamily.regular,
|
|
fontSize: Typography.fontSize.md,
|
|
color: Colors.textSecondary,
|
|
textAlign: 'center',
|
|
marginBottom: Spacing.xl,
|
|
paddingHorizontal: Spacing.lg,
|
|
},
|
|
button: {
|
|
backgroundColor: Colors.primary,
|
|
paddingHorizontal: Spacing.xl,
|
|
paddingVertical: Spacing.md,
|
|
borderRadius: 8,
|
|
marginVertical: Spacing.sm,
|
|
...Shadows.sm,
|
|
minWidth: '80%',
|
|
alignItems: 'center',
|
|
},
|
|
buttonText: {
|
|
color: Colors.background,
|
|
fontSize: Typography.fontSize.md,
|
|
fontFamily: Typography.fontFamily.bold,
|
|
},
|
|
});
|
|
|
|
export default SetupBiometricScreen;
|