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()