Home > Enterprise >  Detect faceID disabled after user declining LAPolicy.deviceOwnerAuthentication
Detect faceID disabled after user declining LAPolicy.deviceOwnerAuthentication

Time:10-25

Is there a way to detect if the user has declined biometrics (faceID) after using "LAContext().evaluatePolicy(.deviceOwnerAuthentication, localizedReason: someReason)"?

For example,

  1. User logs in first time they are prompted with if the app can use their biometrics
  2. User declines biometrics then prompted for passcode
  3. User declines passcode

It appears canEvaluatePolicy returns true for "LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)" until I close and reopen the app.

I want to be able to alert the user that they can turn faceID on in their app settings.

CodePudding user response:

// Declare You're own Error type to handle possible errors
enum BiometryError: Swift.Error, LocalizedError {
    case unavailable, // Biometry unavailable
         failed,      // Can't verify
         cancel,      // User pressed cancel
         failback,    // User choose password
         locked,      // Biometry is locked
         enrolled,    // Biometry not setup
         succsess     // Success

    init(error: LAError) {
        switch error {
        case LAError.authenticationFailed:
            self = .failed
        case LAError.userCancel:
            self = .cancel
        case LAError.userFallback:
            self = .failback
        case LAError.biometryNotAvailable:
            self = .unavailable
        case LAError.biometryNotEnrolled:
            self = .enrolled
        case LAError.biometryLockout:
            self = .locked
        default:
            self = .unavailable
        }
    }
    
    public var errorDescription: String? {
        switch self {
        case .unavailable:
            return "Unavailable"
        case .failed:
            return "Failed"
        case .cancel:
            return "Cancel"
        case .failback:
            return "Failback"
        case .locked:
            return "Locked"
        case .enrolled:
            return "Enrolled"
        case .succsess:
            return "Succsess"

        }
    }
}



    // Authenticate method
    func authenticateBiometry(reply callback: @escaping (Bool, BiometryError?) -> Void) {
            
        LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "reason") { success, error in
            if let error = error as? LAError, !success {
                // Here in this callback You can handle error and show alert if You want
                callback(false, BiometryError(error: error))
            } else  {
                // Success
                callback(true, nil)
            }
        }

    }

CodePudding user response:

There does not appear to be a way to determine the case you describe in your question.

I note that the documentation for canEvaluatePolicy states:

Don’t store the return value from this method because it might change as a result of changes in the system. For example, a user might disable Touch ID after you call this method. However, the reported value does remain consistent until your app enters the background.

However, in testing it seems that even putting the app into the background does not change the value returned by canEvaluatePolicy.

As you note in your question, the value returned does not change until the app is relaunched. You will also see that if you go into preferences and toggle the biometric setting for your app then your app is actually relaunched. This same process happens for other privacy-related settings as well.

You can offer biometric authentication on subsequent launches if you determine that it has been denied, but you should be wary about bugging the user when they have already made a decision.

The other approach you can use is to try LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "reason") and if that fails due to biometrics being denied, retry the authentication with LAContext().evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "reason") which will immediately prompt for a passcode.

  • Related