import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:local_auth/local_auth.dart'; import 'package:shared_preferences/shared_preferences.dart'; enum BiometricAvailability { available, notAvailable, notEnrolled, lockedOut, permanentlyUnavailable; String get message { switch (this) { case BiometricAvailability.available: return 'Biometric authentication is available'; case BiometricAvailability.notAvailable: return 'Biometric authentication is not available on this device'; case BiometricAvailability.notEnrolled: return 'No biometric credentials enrolled on this device'; case BiometricAvailability.lockedOut: return 'Biometric authentication is temporarily locked out'; case BiometricAvailability.permanentlyUnavailable: return 'Biometric authentication is permanently unavailable'; } } } class BiometricService { static const String _biometricEnabledKey = 'biometric_enabled'; static const String _biometricUserIdKey = 'biometric_user_id'; final LocalAuthentication _localAuth = LocalAuthentication(); /// Get display name for biometric type String getBiometricDisplayName(BiometricType type) { switch (type) { case BiometricType.fingerprint: return 'Fingerprint'; case BiometricType.face: return 'Face ID'; case BiometricType.iris: return 'Iris Scanner'; default: return 'Biometric'; } } /// Get emoji for biometric type String getBiometricEmoji(BiometricType type) { switch (type) { case BiometricType.fingerprint: return '👆'; case BiometricType.face: return '👤'; case BiometricType.iris: return '👁️'; default: return '🔒'; } } /// Check if biometric authentication is available Future checkAvailability() async { try { // Check if device supports biometric authentication final canCheckBiometrics = await _localAuth.canCheckBiometrics; if (!canCheckBiometrics) { return BiometricAvailability.notAvailable; } // Check if biometric credentials are enrolled final isDeviceSupported = await _localAuth.isDeviceSupported(); if (!isDeviceSupported) { return BiometricAvailability.notAvailable; } // Try to get available biometric types final availableBiometrics = await _localAuth.getAvailableBiometrics(); if (availableBiometrics.isEmpty) { return BiometricAvailability.notEnrolled; } return BiometricAvailability.available; } on PlatformException catch (e) { if (e.code == 'LockedOut') { return BiometricAvailability.lockedOut; } else if (e.code == 'PermanentlyEnrolled') { return BiometricAvailability.permanentlyUnavailable; } else if (e.code == 'NotAvailable') { return BiometricAvailability.notAvailable; } else if (e.code == 'NotEnrolled') { return BiometricAvailability.notEnrolled; } return BiometricAvailability.notAvailable; } catch (e) { return BiometricAvailability.notAvailable; } } /// Get available biometric types Future> getAvailableBiometrics() async { try { final availableBiometrics = await _localAuth.getAvailableBiometrics(); return availableBiometrics.toList(); } catch (e) { return []; } } /// Check if biometric login is enabled for a user Future isBiometricEnabled() async { try { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_biometricEnabledKey) ?? false; } catch (e) { return false; } } /// Enable biometric login for a user Future enableBiometric(String userId) async { try { // First verify biometric is available final availability = await checkAvailability(); if (availability != BiometricAvailability.available) { return false; } // Test biometric authentication final authenticated = await authenticate( reason: 'Enable biometric login for faster access', localizedReason: 'Enable biometric login for faster access to your 1356 day challenge', ); if (authenticated) { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_biometricEnabledKey, true); await prefs.setString(_biometricUserIdKey, userId); return true; } return false; } catch (e) { return false; } } /// Disable biometric login Future disableBiometric() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.remove(_biometricEnabledKey); await prefs.remove(_biometricUserIdKey); return true; } catch (e) { return false; } } /// Get the user ID associated with biometric login Future getBiometricUserId() async { try { final prefs = await SharedPreferences.getInstance(); return prefs.getString(_biometricUserIdKey); } catch (e) { return null; } } /// Authenticate with biometrics Future authenticate({ String reason = 'Authenticate to access your account', String? localizedReason, bool useErrorDialogs = true, bool stickyAuth = false, bool biometricOnly = true, }) async { try { final authenticated = await _localAuth.authenticate( localizedReason: localizedReason ?? reason, options: AuthenticationOptions( useErrorDialogs: useErrorDialogs, stickyAuth: stickyAuth, biometricOnly: biometricOnly, ), ); return authenticated; } on PlatformException catch (e) { // Handle common biometric errors if (e.code == 'LockedOut') { // User tried too many times return false; } else if (e.code == 'NotAvailable') { // Biometric not available return false; } else if (e.code == 'NotEnrolled') { // No biometric enrolled return false; } else if (e.code == 'OtherOperatingSystem') { // Not supported on this platform return false; } else if (e.code == 'UserFallback') { // User chose to use password instead return false; } return false; } catch (e) { return false; } } /// Get the primary biometric type for display Future getPrimaryBiometricType() async { try { final availableBiometrics = await getAvailableBiometrics(); if (availableBiometrics.contains(BiometricType.face)) { return BiometricType.face; } else if (availableBiometrics.contains(BiometricType.fingerprint)) { return BiometricType.fingerprint; } else if (availableBiometrics.contains(BiometricType.iris)) { return BiometricType.iris; } return null; } catch (e) { return null; } } /// Get user-friendly biometric status message Future getBiometricStatusMessage() async { final availability = await checkAvailability(); final isEnabled = await isBiometricEnabled(); switch (availability) { case BiometricAvailability.available: if (isEnabled) { final type = await getPrimaryBiometricType(); if (type != null) { return '${getBiometricDisplayName(type)} is enabled for quick login'; } return 'Biometric authentication is enabled for quick login'; } else { final type = await getPrimaryBiometricType(); if (type != null) { return '${getBiometricDisplayName(type)} is available but not enabled'; } return 'Biometric authentication is available but not enabled'; } case BiometricAvailability.notAvailable: return 'Biometric authentication is not available on this device'; case BiometricAvailability.notEnrolled: return 'No biometric credentials enrolled on this device'; case BiometricAvailability.lockedOut: return 'Biometric authentication is temporarily locked. Try again later.'; case BiometricAvailability.permanentlyUnavailable: return 'Biometric authentication is permanently unavailable'; } } /// Check if this is a mobile platform that supports biometrics bool get isMobilePlatform { return !kIsWeb && (Platform.isIOS || Platform.isAndroid); } /// Get platform-specific biometric name String getPlatformBiometricName() { if (Platform.isIOS) { return 'Face ID / Touch ID'; } else if (Platform.isAndroid) { return 'Fingerprint / Face Unlock'; } return 'Biometric Authentication'; } }