19

I have the following implementation of LocalAuthentication as described in many places.

context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "Logging in with Touch ID", reply: { (success : Bool, error : NSError? ) -> Void in
        dispatch_async(dispatch_get_main_queue(), {

        if success {
            let alert = UIAlertController(title: "Success", message: "", cancelButtonTitle: "Great!")
            self.presentViewController(alert, animated: true, completion: nil)
        }

        if let error = error {
            var message :String

            switch(error.code) {
            case LAError..AuthenticationFailed:
                message = "There was a problem verifying your identity."
            case LAError..UserCancel:
                message = "You pressed cancel."
            case LAError..UserFallback:
                message = "You pressed password."
            default:
                message = "Touch ID may not be configured"
            }

            let alert = UIAlertController(title: "Error", message: message, cancelButtonTitle: "Darn!")
            self.presentViewController(alert, animated: true, completion: nil)
        }
    })
})

But after I've successfully authenticated with my fingerprint, then evaluatePolicy(, localizedReason:, reply:) returns success without requesting for any fingerprint. I'm actually enabling or disabling TouchID with a UISwitch, so after disabling and re enabling, I'd like to re-authenticate and re enter my fingerprint.

Why is it caching the authentication?

Thanks

Cristian Pena
  • 1,989
  • 16
  • 30

3 Answers3

32

LAContext, once evaluated, will return success until it's deallocated. You can manually invalidate it and then the returned error will be LAError.InvalidContext.

If you want to be prompted with a TouchID confirmation every time, you need to create a LAContext every time. This could be achieved

context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "Logging in with Touch ID", reply: { (success : Bool, error : NSError? ) -> Void in
        dispatch_async(dispatch_get_main_queue(), {

        if success {
            let alert = UIAlertController(title: "Success", message: "", cancelButtonTitle: "Great!")
            self.presentViewController(alert, animated: true, completion: nil)
        }

        if let error = error {
            var message :String

            switch(error.code) {
            case LAError..AuthenticationFailed:
                message = "There was a problem verifying your identity."
            case LAError..UserCancel:
                message = "You pressed cancel."
            case LAError..UserFallback:
                message = "You pressed password."
            default:
                message = "Touch ID may not be configured"
            }

            let alert = UIAlertController(title: "Error", message: message, cancelButtonTitle: "Darn!")
            self.presentViewController(alert, animated: true, completion: nil)
        }

        context = LAContext()
    })
})
Cristian Pena
  • 1,989
  • 16
  • 30
  • Great answer, it cleared situation, thanks. There is no any word about it in Apple docs, and phrase: "Don’t assume that a previous successful policy evaluation means that future evaluations will also succeed. Policy evaluation can fail for various reasons, including cancellation by the user or the system." really confusing. – Dren Dec 05 '17 at 17:21
  • Using `touchIDAuthenticationAllowableReuseDuration` is the intended (and better) solution to this problem. – Matjan Apr 05 '18 at 18:05
9

Since ios 9 there is touchIDAuthenticationAllowableReuseDuration for the context

The duration for which Touch ID authentication reuse is allowable. If the device was successfully authenticated using Touch ID within the specified time interval, then authentication for the receiver succeeds automatically, without prompting the user for Touch ID. The default value is 0, meaning that Touch ID authentication cannot be reused. The maximum allowable duration for Touch ID authentication reuse is specified by the LATouchIDAuthenticationMaximumAllowableReuseDuration constant. You cannot specify a longer duration by setting this property to a value greater than this constant. Availability iOS (9.0 and later), macOS (10.12 and later)

if you set for example to 60

context.touchIDAuthenticationAllowableReuseDuration = 60

It will auto succeed without checking, if the user has successfully passed the touch id checking in the last 60 secs.

So, you can set to the value that suites you. I find it a good very good and it's annoying to ask the user to touch again while he just did it a few seconds ago.(to unlock the screen for example).

Ismail
  • 2,633
  • 2
  • 21
  • 39
  • Note that as of iOS 13.2 SDK the documentation reads "If the device was successfully _**unlocked**_ by biometry within this time interval, (...)". – Olivier Jun 18 '20 at 16:23
2

I was facing the same problem but then I increased the duration value as below:

context.touchIDAuthenticationAllowableReuseDuration = Double(5 * 60) // 5 min, 

This solution worked for me.

Naeem Ul Wahhab
  • 2,299
  • 4
  • 29
  • 57
Aman
  • 51
  • 1
  • 4
  • You could have used the constant LATouchIDAuthenticationMaximumAllowableReuseDuration because it is the maximum Allowed time interval. Its value is 5 min only. – Ansari Awais Dec 04 '17 at 05:16