auth.swift

 1import Foundation
 2import LocalAuthentication
 3
 4// Compilation: swiftc auth.swift -o auth
 5// Usage: ./auth <reason>
 6
 7enum AuthResult: String, Codable {
 8    case success
 9    case failure
10    case notAvailable
11    case userCancel
12    case fallback
13}
14
15struct Response: Codable {
16    let status: AuthResult
17    let message: String?
18}
19
20func authenticate(reason: String) {
21    let context = LAContext()
22    var error: NSError?
23
24    // Check if biometric authentication is available on the device.
25    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
26        
27        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
28            
29            DispatchQueue.main.async {
30                if success {
31                    sendResponse(status: .success, message: "Successfully authenticated")
32                } else {
33                    if let error = authenticationError as? LAError {
34                        switch error.code {
35                        case .userCancel:
36                            sendResponse(status: .userCancel, message: "User cancelled")
37                        case .userFallback:
38                            sendResponse(status: .fallback, message: "User chose fallback")
39                        case .biometryNotEnrolled:
40                            sendResponse(status: .notAvailable, message: "Biometry not enrolled")
41                        case .biometryLockout:
42                            sendResponse(status: .failure, message: "Biometry lockout")
43                        default:
44                            sendResponse(status: .failure, message: error.localizedDescription)
45                        }
46                    } else {
47                        sendResponse(status: .failure, message: "Unknown error")
48                    }
49                }
50            }
51        }
52    } else {
53        // Biometry is not available on this device
54        let message = error?.localizedDescription ?? "Biometry not available"
55        sendResponse(status: .notAvailable, message: message)
56    }
57}
58
59func sendResponse(status: AuthResult, message: String?) {
60    let response = Response(status: status, message: message)
61    if let data = try? JSONEncoder().encode(response),
62       let jsonString = String(data: data, encoding: .utf8) {
63        print(jsonString)
64    }
65    exit(0)
66}
67
68let args = ProcessInfo.processInfo.arguments
69let reason = args.count > 1 ? args[1] : "Authenticate to unlock Matcha"
70
71authenticate(reason: reason)
72
73// Keep the process alive for the async evaluation
74RunLoop.main.run()