How to accept Touch ID on macOS without triggering a Prompt

216 views Asked by At

Is there a way to accept Touch ID, without triggering the default MacOS Touch ID prompt? Or is there a way to heavily customize the default Touch ID prompt?

I have seen 1Password do it, so it is possible.

I have tried following these tutorials, but they didn't work

Accessing Keychain Items with Face ID or Touch ID

Logging a User into Your App with Face ID or Touch ID

I have also read about Passkeys, but I don't think it will work because it's for Web Authentication.

Any pointers in the right direction will be greatly appreciated!

This is a screenshot of how 1Password does it:

enter image description here

1

There are 1 answers

4
Michael Haephrati On

What you are trying to do isn't supported, at least not officially. You can try using custom UI overlay. The code is in swift.

import SwiftUI
import LocalAuthentication

struct ContentView: View {
    @State private var authenticationResult: AuthenticationResult?

    var body: some View {
        VStack {
            Text("Touch ID Authentication (add your custom text here)")
                .padding()

            Button("Authenticate with Touch ID") {
                authenticateWithTouchID()
            }
            .padding()

            if let result = authenticationResult {
                CustomResultView(result: result)
                    .padding()
            }
        }
    }

    func authenticateWithTouchID() {
        let context = LAContext()

        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Authenticate using Touch ID") { success, error in
                DispatchQueue.main.async {
                    if success {
                        authenticationResult = .success
                    } else {
                        authenticationResult = .failure
                    }
                }
            }
        } else {
            authenticationResult = .notAvailable
        }
    }
}

struct CustomResultView: View {
    let result: AuthenticationResult

    var body: some View {
        VStack {
            switch result {
            case .success:
                Image(systemName: "checkmark.circle")
                    .foregroundColor(.green)
                    .font(.system(size: 40))
                Text("Authentication Successful")
                    .foregroundColor(.green)

            case .failure:
                Image(systemName: "xmark.circle")
                    .foregroundColor(.red)
                    .font(.system(size: 40))
                Text("Authentication Failed")
                    .foregroundColor(.red)

            case .notAvailable:
                Image(systemName: "exclamationmark.circle")
                    .foregroundColor(.orange)
                    .font(.system(size: 40))
                Text("Touch ID not available or not configured")
                    .foregroundColor(.orange)
            }
        }
    }
}

enum AuthenticationResult {
    case success
    case failure
    case notAvailable
}

@main
struct TouchIDExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}