import Foundation import LocalAuthentication enum AuthenticationError: LocalizedError { case biometricsNotAvailable case authenticationFailed(String) case userCancelled var errorDescription: String? { switch self { case .biometricsNotAvailable: return "Touch ID is not available on this device." case .authenticationFailed(let reason): return "Authentication failed: \(reason)" case .userCancelled: return "Authentication was cancelled." } } } struct AuthenticationService: Sendable { /// Attempts biometric authentication (Touch ID) before proceeding. /// Falls back gracefully if Touch ID is not available. /// Returns true if authenticated, false if biometrics unavailable (will proceed to admin dialog). func authenticateWithBiometrics() async throws -> Bool { let context = LAContext() var error: NSError? guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else { // Touch ID not available — skip biometric step, will use admin dialog return true } do { let success = try await context.evaluatePolicy( .deviceOwnerAuthenticationWithBiometrics, localizedReason: "Authenticate to modify system TTL value" ) return success } catch let authError as LAError { switch authError.code { case .userCancel, .appCancel: throw AuthenticationError.userCancelled case .userFallback: // User chose to use password — allow proceeding to admin dialog return true default: throw AuthenticationError.authenticationFailed(authError.localizedDescription) } } } }