If you are developing an app that deals with sensitive user information, such as financial or health, you may want to detect if a user added/removed a fingerprint from TouchID, or added/removed a face from FaceID. This can prevent a situation where someone gets ahold of the user’s device, then adds their own biometrics identification in order to get into the app.
Since iOS 9, The LAContext
class in the LocalAuthentication
framework provides a way to check for this type of change.
There is a property on an LAContext
instance named evaluatedPolicyDomainState
, which tells us the current state of the policy that was evaluated. In terms of biometrics, it’s a piece of data that represents what face(s) or fingerprint(s) are currently enrolled. So, comparing the current state to an earlier saved state will tell us whether something has changed on what identification has been enrolled.
What we’ll do is add an extension to LAContext
that provides way to store the state data in User Defaults, and a method to check if the value has changed.
extension LAContext {
static var savedBiometricsPolicyState: Data? {
get {
UserDefaults.standard.data(forKey: "BiometricsPolicyState")
}
set {
UserDefaults.standard.set(newValue, forKey: "BiometricsPolicyState")
}
}
static func biometricsChanged() -> Bool {
let context = LAContext()
var error: NSError?
context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
// If there is no saved policy state yet, save it
if error == nil && LAContext.savedBiometricsPolicyState == nil {
LAContext.savedBiometricsPolicyState = context.evaluatedPolicyDomainState
return false
}
if let domainState = context.evaluatedPolicyDomainState, domainState != LAContext.savedBiometricsPolicyState {
return true
}
return false
}
}
We can now use this function to determine if biometrics enrollment has changed whenever a user logs in.
func handleLogIn() {
let context = LAContext()
// Check first that biometrics is available
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) else {
// Handle not being able to use biometrics
return
}
guard !LAContext.biometricsChanged() else {
// Handle biometrics changed, such as clearing credentials and making the user manually log in
// Reset LAContext.savedBiometricsPolicyState to nil after doing so
return
}
// Log in with biometrics
let reason = "Sign in to your account"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason ) { success, error in
if success {
LAContext.savedBiometricsPolicyState = context.evaluatedPolicyDomainState
DispatchQueue.main.async {
// Go to next screen after login success
}
} else {
// Handle error
}
}
}