Implement Face ID Authentication in the iOS App

Enhance your iOS app's security and user experience with FaceID

·

8 min read

Implement Face ID Authentication in the iOS App

Exciting News! Our blog has a new home! 🚀

Introduction

Biometric authentication has transformed mobile security, and Apple’s Face ID, introduced with the iPhone X in 2017, stands at the forefront of this revolution.

Utilizing advanced facial recognition technology, Face ID goes beyond traditional methods by leveraging depth-sensing cameras and machine learning algorithms.

Stored securely within the device’s Secure Enclave, Face ID creates a mathematical representation of facial features, ensuring unparalleled security. Moreover, its seamless integration into iOS apps has redefined user interactions, setting new standards for mobile authentication.

In this blog, we’ll delve into the process of integrating Face ID into iOS apps to enhance both security and user experience.

Understanding Face ID

Face ID replaces the conventional Touch ID with a sophisticated facial recognition system, providing users with a secure and effortless means of unlocking their devices and validating actions.

Utilizing the TrueDepth camera system, Face ID captures and analyzes facial features in real-time, generating a precise 3D map.

This map, created from thousands of infrared dots projected onto the face, forms a unique Faceprint and is securely stored within the device’s Secure Enclave.

During authentication, Face ID compares the current facial data with the stored Faceprint, utilizing machine learning to adapt to changes in appearance and ensure consistent performance.

In essence, Face ID represents a user-friendly authentication solution that integrates cutting-edge technology to redefine mobile security and user experience standards on iOS devices.

1. Face ID’s Advantages

Face ID presents a significant leap forward in security and convenience compared to traditional password or PIN-based authentication methods.

By relying on facial recognition technology, it eliminates the need for manual input, offering users a seamless and hands-free authentication experience.

Furthermore, the inherent uniqueness of facial features makes Face ID exceptionally secure, greatly reducing the risk of phishing attacks.

2. Addressing Concerns

Misconceptions surrounding Face ID’s security and privacy have led to concerns about data storage and privacy risks.

However, it’s crucial to understand that Face ID securely stores facial data locally on the device, making it inaccessible to apps or cloud services.

Additionally, it continually improves its performance through machine learning algorithms, enhancing security measures while safeguarding user privacy.

Since Face ID operates solely on the device, it doesn’t transmit facial data externally, mitigating privacy concerns and ensuring user data remains protected.

Requirements & Prerequisites

To successfully integrate Face ID into iOS apps, developers need to meet specific hardware, software, and prerequisite criteria.

1. Hardware Requirements

Ensure the device includes a TrueDepth camera system (a key component for Face ID functionality) available on iPhone X and later models.

2. Software Requirements

Face ID integration requires a minimum iOS version compatible with the TrueDepth camera, typically iOS 11 or later.

3. Prerequisites

1. Privacy Considerations: Developers should dhere to Apple's privacy guidelines and obtain user consent when accessing biometric data for authentication.

2. App Capabilities: Ensure that the app's Info.plist file includes the necessary permissions and descriptions for accessing FaceID usage description. This is crucial for informing users about why the app requires Face ID access.

Integration Steps

To access the faceID into an iOS app involves several key steps:

Update Info.plist: Add ‘Privacy — Face ID Usage Description’ to the app’s Info.plist file. This string value informs users about the app’s use of Face ID and should be concise yet informative.
Note: Keep the key as sort as possible which allow user to undestand the purpose of permission.

Create Authenticator: Create an authenticator within the app that uses Face ID APIs to authenticate users. Import the ‘LocalAuthentication’ module, which provides access to authentication-related APIs.
Note: The app’s Info.plist must contain an NSFaceIDUsageDescription key with a string value explaining to the user how the app uses this data.

1. Availability Check

Here we need to check whether the device supports biometric authentication or not, to do that we have created an extension of LAContext which provides the status of availability.

extension LAContext {
    enum BiometricType: String {
        case none
        case touchID
        case faceID
        case opticID // Added a case for potential future biometric methods
    }

    // Returns the detected biometric type based on the capabilities of the device
    var biometricType: BiometricType {
        var error: NSError?

        // Check if the device supports biometric authentication
        guard self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            return .none
        }

        if #available(iOS 11.0, *) {
            // Determine the specific biometric type supported on iOS 11.0 and later
            switch self.biometryType {
            case .none:
                return .none
            case .touchID:
                return .touchID
            case .faceID:
                return .faceID
            default:
                if #available(iOS 17.0, *) {
                    // Support for potential future biometric methods
                    if self.biometryType == .opticID {
                        return .opticID
                    } else {
                        return .none
                    }
                }
            }
        }

        // Fallback to checking for Touch ID support on iOS versions older than 11.0
        return self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
    }
}

This extension introduces an enum BiometricType, enabling developers to determine the supported biometric authentication method on iOS devices.

It checks for the device supports for the biometric authentication using canEvaluatePolicy(_:error:). If biometric authentication is not available, it returns .none.

For devices running iOS 11.0 and later, it utilizes the biometryType property of LAContext to determine the supported biometric type.

Additionally, it includes a case for potential future biometric methods, such as optic scanning, and provides a fallback mechanism for devices running iOS versions older than 11.0.

2. Implementing authentication

For authentication, we have created an Authenticator class which manages all the authentication-related things.

In this class, we have the authenticate() method to authenticate whether the user is verified or not and it will act accordingly.

private func authenticate() {

    // Check if biometric authentication is available and not locked
    guard isBiometricAvailable() && !isBiometricLocked else { return }

    isLoading = true
    var error: NSError?

    // Check if biometric authentication is possible
    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {

        // Perform biometric authentication
        let reason = "We need to unlock your data."

        // It presents a localized reason for authentication to the user, explaining why authentication is necessary.
        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { [weak self]
            success, authenticationError in
            guard let self else { return }

            // Handle authentication completion
            if success {
                // proceeds to decrypt and verify the user's passcode
                let passcode = self.decryptUserPasscode()
                self.isAuthenticated = passcode != nil
            } else {
                // increments the failedAttempt count
                self.failedAttempt += 1

                // check for the maximum allowed failed limit
                self.isBiometricLocked = self.failedAttempt >= self.maxFailedAttemptAllowed
            }
            self.isLoading = false
        }
    } else {
        // Handle error if biometric authentication is not possible
        if let error {
            handleLaError(error: error)
        } else {
            // Handle other errors if any
        }
    }
}

This authenticate() function is responsible for initiating the biometric authentication process.

  • It verifies the availability and unlock status of biometric authentication using canEvaluatePolicy. If it's possible, it proceeds with it and handles success or failure accordingly.

  • If it succeeds move ahead otherwise we check for the failed limit, if it reaches the maximum limit then basically we disable the biometric authentication temporarily.

  • If biometric authentication is unavailable, it checks for the error. If an error is present, it handles the error appropriately.

This function provides a structured approach to biometric authentication, handling success, failure, and error scenarios gracefully while ensuring a smooth user experience.

  1. If you ask for the deviceOwnerAuthenticationWithBiometrics policy then you will have to handle the fallback yourself.

  2. If you ask for deviceOwnerAuthentication only, then biometrics will be used if available and authorized, otherwise, it will automatically fall back to passcode if biometrics is unavailable, or give you the fallback option to enter passcode automatically if biometrics attempt fails.

3. Secure Passcode

func setPasscodeWith(_ code: String) {
    // Check if biometric authentication is available
    guard isBiometricAvailable() else { return }

    // Generate a unique encryption key
    let key = UUID().uuidString // Instead of this random string you can use a key that can be recoverable

    // Encrypt the passcode using AES encryption algorithm
    let encryptedPasscode = AESEncryptionManager.encrypt(plainText: code, key: key)

    // Store the encrypted passcode and encryption key securely
    userDefault.setValue(encryptedPasscode, forKey: userDefaultPasscodeKey)
    userDefault.setValue(key, forKey: userDefaultSecretKey)
}

This Swift function setPasscodeWith(_:) is responsible for setting a passcode using biometric authentication, if available.

Here's a breakdown of its functionality:

  1. It checks for biometric authentication availability.

  2. It generates a unique encryption key using UUID().uuidString that will be used to encrypt the passcode securely.

  3. It encrypts the provided passcode using the generated encryption key. The AESEncryptionManager.encrypt(plainText:key:) function encrypts the passcode using the AES encryption algorithm.

  4. It stores the encrypted passcode and the encryption key securely in the user defaults.

The encrypted passcode is stored with a key userDefaultPasscodeKey and the encryption key is stored with a key userDefaultSecretKey, This ensures that the passcode remains stored on the device.

If you are not aware about AES encryption then here is the good Article on it which explains AES encryption in detail.

This post only has Authentication implementation steps, to read the complete guide including Handling Fallback and Testing & Debugging implementation please visit this blog.

The post is originally published on canopas.com.

If you like what you read, be sure to hit
💖 button below! — as a writer it means the world!

I encourage you to share your thoughts in the comments section below. Your input not only enriches our content but also fuels our motivation to create more valuable and informative articles for you.

Follow Canopas to get updates on interesting articles!

Did you find this article valuable?

Support Canopas's blog by becoming a sponsor. Any amount is appreciated!