import AsyncStorage from '@react-native-async-storage/async-storage'; import * as LocalAuthentication from 'expo-local-authentication'; import { Platform } from 'react-native'; /** * Maneja la autenticación biométrica (Face ID o huella). * @returns {Promise} Retorna `true` si la autenticación es exitosa, `false` en caso contrario. */ export const handleBiometricAuth = async () => { // 1. Verificar si el hardware del dispositivo es compatible const isCompatible = await LocalAuthentication.hasHardwareAsync(); if (!isCompatible) { return { success: false, message: 'Dispositivo no compatible con biometría', code: 'BiometricNotSupported', error: null, }; } // 2. Verificar si hay huellas o rostros registrados en el dispositivo const isEnrolled = await LocalAuthentication.isEnrolledAsync(); if (!isEnrolled) { // Alert.alert( // 'Autenticación Biométrica', // 'No tienes ninguna credencial biométrica guardada. Por favor, configura Face ID o tu huella en los ajustes de tu dispositivo.' // ); return { success: false, message: 'No hay credenciales biométricas registradas', code: 'BiometricNotEnrolled', error: null, }; } // 3. Iniciar la autenticación const { success } = await LocalAuthentication.authenticateAsync({ promptMessage: 'Autentícate para acceder a tu certificado', // Mensaje que se muestra en el diálogo cancelLabel: 'Cancelar', // Texto del botón de cancelar (opcional en Android) disableDeviceFallback: false, // Evita que se pueda usar el código del dispositivo como alternativa }); if (success) { // console.log('¡Autenticación exitosa! ✅'); return { success: true, message: 'Autenticación exitosa', code: 'BiometricSuccess', error: null, }; } else { // console.log('Autenticación fallida. ❌'); return { success: false, message: 'Autenticación fallida', code: 'BiometricFailed', error: null, }; } }; // Usamos una clave constante para evitar errores de tipeo const BIOMETRIC_USERS_KEY = 'APACUANA_BIOMETRIC_ENABLED_USERS'; /** * Agrega un userId a la lista de usuarios con biometría habilitada. * @param {string} userId - El ID del usuario a agregar. */ export const addUserWithBiometrics = async (userId: string) => { try { // 1. Obtener la lista existente const existingUsers = await AsyncStorage.getItem(BIOMETRIC_USERS_KEY); const users = existingUsers ? JSON.parse(existingUsers) : []; // 2. Agregar el nuevo userId solo si no está ya en la lista if (!users.includes(userId)) { users.push(userId); // 3. Guardar la lista actualizada en formato string await AsyncStorage.setItem(BIOMETRIC_USERS_KEY, JSON.stringify(users)); // console.log(`✅ Usuario ${userId} agregado a la lista de biometría.`); return { success: true, message: 'Usuario agregado a la lista de biometría', code: 'BiometricUserAdded', error: null, }; } else { // console.log(`ℹ️ El usuario ${userId} ya estaba en la lista.`); return { success: false, message: 'El usuario ya estaba en la lista de biometría', code: 'BiometricUserAlreadyExists', error: null, }; } } catch (error) { console.error( '[ApacuanaSDKError]: Error al guardar el usuario con biometría:', error ); return { success: false, message: 'Error al guardar el usuario con biometría', code: 'BiometricStorageError', error: error, }; } }; /** * Elimina un userId de la lista de usuarios con biometría habilitada. * @param {string} userId - El ID del usuario a eliminar. */ export const removeUserWithBiometrics = async (userId: string) => { if (!userId) { return { success: false, message: 'userId no proporcionado', code: 'BiometricInvalidUserId', error: null, }; } try { // 1. Obtener la lista existente const existingUsers = await AsyncStorage.getItem(BIOMETRIC_USERS_KEY); let users = existingUsers ? JSON.parse(existingUsers) : []; // 2. Filtrar la lista para excluir el userId const updatedUsers = users.filter((id: string) => id !== userId); // 3. Guardar la nueva lista actualizada await AsyncStorage.setItem( BIOMETRIC_USERS_KEY, JSON.stringify(updatedUsers) ); // console.log(`✅ Usuario ${userId} eliminado de la lista de biometría.`); return { success: true, message: 'Usuario eliminado de la lista de biometría', code: 'BiometricUserRemoved', error: null, }; } catch (error) { // console.error('Error al eliminar el usuario con biometría:', error); return { success: false, message: 'Error al eliminar el usuario con biometría', code: 'BiometricUserRemovalError', error: error, }; } }; /** * Obtiene la lista de todos los userIds con biometría habilitada. * @returns {Promise} Una promesa que resuelve a un array de userIds. */ export const getUsersWithBiometrics = async () => { try { const users = await AsyncStorage.getItem(BIOMETRIC_USERS_KEY); return users ? JSON.parse(users) : []; } catch (error) { console.error('Error al obtener los usuarios con biometría:', error); return []; } }; /** * Verifica si un usuario específico tiene la biometría habilitada. * @param {string} userId - El ID del usuario a verificar. * @returns {Promise} Retorna `true` si el usuario tiene biometría, `false` en caso contrario. */ export const isBiometricEnabledForUser = async (userId: string) => { if (!userId) { return { success: false, message: 'userId no proporcionado', code: 'BiometricInvalidUserId', error: null, }; } try { const isCompatible = await LocalAuthentication.hasHardwareAsync(); if (!isCompatible) { return { success: false, message: 'Dispositivo no compatible con biometría', code: 'BiometricNotSupported', error: null, }; } // 2. Verificar si hay huellas o rostros registrados en el dispositivo const isEnrolled = await LocalAuthentication.isEnrolledAsync(); if (!isEnrolled) { return { success: false, message: 'No hay credenciales biométricas registradas', code: 'BiometricNotEnrolled', error: null, }; } // 1. Obtiene la lista completa de usuarios con biometría const usersWithBiometrics = await getUsersWithBiometrics(); // 2. Comprueba si el userId actual está en esa lista return { success: true, message: 'Verificación exitosa', code: 'BiometricCheckSuccess', error: null, isEnabled: usersWithBiometrics.includes(userId), }; } catch (error) { console.error('Error al verificar la biometría para el usuario:', error); return { success: false, message: 'Error al verificar la biometría para el usuario', code: 'BiometricCheckError', error: error, }; } }; export const isBiometricEnabled = async () => { try { const isCompatible = await LocalAuthentication.hasHardwareAsync(); if (!isCompatible) { return { success: false, message: 'Dispositivo no compatible con biometría', code: 'BiometricNotSupported', error: null, }; } // 2. Verificar si hay huellas o rostros registrados en el dispositivo const isEnrolled = await LocalAuthentication.isEnrolledAsync(); if (!isEnrolled) { return { success: false, message: 'No hay credenciales biométricas registradas', code: 'BiometricNotEnrolled', error: null, }; } return true; } catch (error) { console.error('Error al verificar la biometría:', error); return { success: false, message: 'Error al verificar la biometría', code: 'BiometricCheckError', error: error, }; } }; /** * Verifica el tipo de hardware biométrico disponible en el dispositivo. * Da prioridad a Face ID si ambos estuvieran disponibles (algo muy improbable). * * @returns {Promise<'Face ID' | 'Touch ID' | false>} Una promesa que se resuelve a: * - "Face ID" si el dispositivo soporta reconocimiento facial. * - "Touch ID" si el dispositivo soporta huella digital. * - false si no soporta ninguno, no tiene hardware, o si ocurre un error. */ export const getBiometricType = async (): Promise< 'Face ID' | 'Touch ID' | false > => { try { if (Platform.OS === 'android') return 'Touch ID'; // Primero, verificamos que el dispositivo tenga CUALQUIER hardware biométrico. const hasHardware = await LocalAuthentication.hasHardwareAsync(); if (!hasHardware) { return false; } // Si tiene hardware, verificamos qué tipos específicos soporta. const supportedTypes = await LocalAuthentication.supportedAuthenticationTypesAsync(); // Damos prioridad a Face ID, ya que es el método más moderno en dispositivos Apple. if ( supportedTypes.includes( LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION ) ) { return 'Face ID'; } // Si no tiene Face ID, verificamos si tiene lector de huella. if ( supportedTypes.includes( LocalAuthentication.AuthenticationType.FINGERPRINT ) ) { return 'Touch ID'; } // Si tiene hardware pero no es de un tipo conocido (muy raro), retornamos false. return false; } catch (error) { console.error('Error al verificar el tipo de biometría:', error); // Si hay algún error durante la comprobación, asumimos que no hay biometría disponible. return false; } };