Implementing Touch ID and Face ID Authentication in iOS 11 Apps
Learn SwiftUI and take your iOS Development to the Next Level |
In the world of computer security, user authentication falls into the three categories of something you know, something you have and something you are. The "something you know" category typically involves a memorized password or PIN number and is considered the least secure option. A more secure option is the "something you have" approach which usually takes the form of a small authentication token or device which generates one-time access codes on request.
The final category, "something you are", refers to a physical attribute that is unique to the user. This, of course, involves biometrics in the form of a retina scan, facial or voice recognition or fingerprint.
With the iPhone 5s, Apple introduced a built-in fingerprint scanner which enabled users to access the device and make purchases in the iTunes, App and iBooks stores using fingerprint authentication. Since the introduction of iOS 8, this biometric authentication capability can now be built into your own applications. With the introduction of the iPhone X and iOS 11, biometric authentication using facial recognition can also be built into your iOS apps.
The Local Authentication Framework
Biometric authentication for iOS applications is implemented using the Local Authentication Framework. The key class within this framework is the LAContext class which, among other tasks, is used to evaluate the authentication abilities of the device on which the application is running and perform the authentication.
Checking for Biometric Authentication Availability
Not all iOS devices have the fingerprint scanner or facial recognition and, even on devices with the necessary hardware support, not all users will have activated these authentication features. The first step in using biometric authentication, therefore, is to check that biometric authentication is a viable option on the device:
let context = LAContext() var error: NSError? if context.canEvaluatePolicy( LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) { // Biometry is available on the device } else { // Biometry is not available on the device // No hardware support or user has not set up biometric auth }
If biometric authentication is not available, the reason can be identified by accessing the errorCode property of the error parameter and will fall into one of the following categories:
- LAError.biometryNotEnrolled - The user has not enrolled in biometric authentication on the device.
- LAError.passcodeNotSet - The user has not yet configured a passcode on the device.
- LAError.biometryNotAvailable - The device does not have the required biometric hardware support.
Identifying Authentication Options
In the event that the device on which the app is running contains the necessary biometric hardware, it can be useful to identify the type of authentication that is supported. This can be achieved by evaluating the biometryType property of the LAContext instance as follows:
let context = LAContext() var error: NSError? if context.canEvaluatePolicy( LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) { if (context.biometryType == LABiometryType.faceID) { // Device support Face ID } else if context.biometryType == LABiometryType.touchID { // Device supports Touch ID } else { // Device has no biometric support }
Evaluating Biometric Policy
In the event that biometric authentication is available, the next step is to evaluate the policy. This task is performed by calling the evaluatePolicy method of the LAContext instance, passing through the authentication policy type and a message to be displayed to the user. The task is performed asynchronously and a reply closure expression called once the user has provided input:
context.evaluatePolicy( LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Authentication is required for access", reply: {(success, error) in // Code to handle reply here })
The reply closure expression is passed a Boolean value indicating the success or otherwise of the authentication and an NSError object from which the nature of any failure can be identified via the corresponding error code. Failure to authenticate will fall into one of the following three categories:
- LAError.systemCancel - The authentication process was cancelled by the operating system. This error typically occurs when the application is placed in the background.
- LAError.userCancel - The authentication process was cancelled by the user.
- LAError.userFallback - The user opted to authenticate using a password instead of using Touch or Face ID.
In the event of the user fallback, it is the responsibility of the application to prompt for and verify a password before providing access.
If the authentication process is successful, however, the application should provide the user with access to whatever screens, data or functionality were being protected.
A Biometric Authentication Example Project
Launch Xcode and create a new iOS Single View Application project named BiometricID using Swift as the programming language.
Select the Main.storyboard file and drag and drop a Button view so that it is positioned in the center of the storyboard scene. Change the text on the button so that it reads Authenticate.
With the button selected, display the Auto Layout Align menu and configure both horizontal and vertical center in container constraints.
Display the Assistant Editor, Ctrl-click on the button view and drag the resulting line to a point beneath the ViewController class declaration line. On releasing the line, establish a connection to an outlet named authButton.
Finally, Ctrl-click and drag from the button view to a position just beneath the viewDidLoad method in the ViewController.swift file. Release the line and, in the connection dialog, establish an Action connection to a method named authenticateUser.
On completion of the user interface design the layout should resemble Figure 63-1:
[[File:]]
Figure 63-1
Checking for Biometric Availability
With the user interface designed, the next step is to add some code to the authenticateUser method to verify that the device can handle biometric authentication. Select the ViewController.swift file, import the LocalAuthentication Framework and add code to the authenticateUser method as follows:
import UIKit import LocalAuthentication class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } @IBAction func authenticateUser(_ sender: Any) { let context = LAContext() var error: NSError? if context.canEvaluatePolicy( LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) { // Device can use biometric authentication } else { // Device cannot use biometric authentication if let err = error { switch err.code{ case LAError.Code.biometryNotEnrolled.rawValue: notifyUser("User is not enrolled", err: err.localizedDescription) case LAError.Code.passcodeNotSet.rawValue: notifyUser("A passcode has not been set", err: err.localizedDescription) case LAError.Code.biometryNotAvailable.rawValue: notifyUser("Biometric authentication not available", err: err.localizedDescription) default: notifyUser("Unknown error", err: err.localizedDescription) } } } }
In addition to evaluating authentication policy, the above code also identifies whether the device supports Face ID or Touch ID authentication and updates the text displayed on the authButton instance accordingly.
Before proceeding, implement the notifyUser method as follows:
func notifyUser(_ msg: String, err: String?) { let alert = UIAlertController(title: msg, message: err, preferredStyle: .alert) let cancelAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) alert.addAction(cancelAction) self.present(alert, animated: true, completion: nil) }
Seeking Biometric Authentication
The next task is to attempt to obtain authentication from the user. This involves a call to the evaluatePolicy method of the local authentication context:
@IBAction func authenticateUser(_ sender: Any) { let context = LAContext() var error: NSError? if context.canEvaluatePolicy( LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) { // Device can use biometric authentication context.evaluatePolicy( LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Access requires authentication", reply: {(success, error) in DispatchQueue.main.async { if let err = error { switch err._code { case LAError.Code.systemCancel.rawValue: self.notifyUser("Session cancelled", err: err.localizedDescription) case LAError.Code.userCancel.rawValue: self.notifyUser("Please try again", err: err.localizedDescription) case LAError.Code.userFallback.rawValue: self.notifyUser("Authentication", err: "Password option selected") // Custom code to obtain password here default: self.notifyUser("Authentication failed", err: err.localizedDescription) } } else { self.notifyUser("Authentication Successful", err: "You now have full access") } } }) } else { // Device cannot use biometric authentication if let err = error { switch err.code { case LAError.Code.biometryNotEnrolled.rawValue: notifyUser("User is not enrolled", err: err.localizedDescription) case LAError.Code.passcodeNotSet.rawValue: notifyUser("A passcode has not been set", err: err.localizedDescription) case LAError.Code.biometryNotAvailable.rawValue: notifyUser("Biometric authentication not available", err: err.localizedDescription) default: notifyUser("Unknown error", err: err.localizedDescription) } } } }
The code added to the method initiates the authentication process and displays a message confirming a successful authentication. In the event of an authentication failure, a message is displayed to the user indicating the reason for the failure. Selection of the password option simply confirms that the option was selected. The actual action taken in this situation will be application specific but will likely involve prompting for a password and verifying it against a database of valid passwords.
Adding the Face ID Privacy Statement
The final step before testing is to configure the Face ID privacy statement within the project's Info.plist file. This is the statement that is displayed to the user when the app seeks permission to use Face ID authentication. To add this entry, select the Info.plist file in the project navigator panel and click on the + button located in the bottom entry of the list. From the resulting menu, select the Privacy - Face ID Usage Description option and enter a description into the value field:
[[File:]]
Figure 63-2
Testing the Application
Biometric authentication can be tested either on physical iOS devices that include biometric support, or using the simulator environment.
When testing Face ID support on a simulator, compile and run the app on an iPhone X simulator. Once the app has launched, select the simulator's Hardware -> Face ID menu and make sure the Enrolled option is enabled (as highlighted in Figure 63 3) before tapping the Authenticate button within the app.
[[File:]]
Figure 63-3
At this point, the Face ID permission request dialog will appear displaying the privacy statement that was previously entered into the Info.plist file as shown in Figure 63-4:
[[FIle:]]
Figure 63-4
After granting permission by clicking on the OK button, the gray simulated Face ID panel (Figure 63-5) should appear:
[[File:]]
Figure 63 5
To simulate a matching face, select the Hardware -> Face ID -> Matching Face menu option after which the app should display the dialog shown in Figure 63-6 indicating that the authentication was successful:
[[File:]]
Figure 63 6
Repeat the authentication process, this time selecting the Non-matching menu option and verify that the authentication fails.
Launch the app on a physical device with Touch ID support, or use a suitable simulator instance (for example an iPhone 8) to test Touch ID authentication. If using a simulator, make sure that the Hardware -> Touch ID -> Enrolled option is enabled before clicking the Authenticate button. When instructed to touch the home button, select the Hardware -> Touch ID -> Matching Touch menu option to test the authentication. After a successful authentication, try again using the Non-matching Touch option.
Summary
Introduced with iOS 7 and the iPhone 5s device, Touch ID has been provided to iOS users as a way to gain access to devices and to make Apple related purchases. Since the introduction of iOS 8, fingerprint authentication using the Touch ID system has been available to application developers. With the introduction of iOS 11 running on the iPhone X, authentication support has been extended to include facial recognition. This chapter has outlined the steps involved in using the Local Authentication Framework to implement biometric authentication using both the Touch ID and Face ID systems.