HeadSpin Documentation
Documentation

Testing Touch and Face ID in iOS

Biometric Testing in iOS

Since iOS 8.0, biometric authentication mechanisms such as Touch ID and Face ID (in iOS 11.0) have been introduced to securely authenticate in a streamlined manner. Instrumenting tests which involve these biometrics process presents a challenge due to the physical interaction with the fingerprint sensor or the depth camera for Face ID used for authentication.

HeadSpin offers a biometric instrumentation SDK with convenient classes that easily integrate into your app's code that work with our API endpoints and remote control web UI to help automating biometric tests.

Note Regarding iOS 17

Currently HeadSpin does not support the use of biometric testing with devices running iOS 17. When this functionality is enabled, this section will be updated. For more information on what is and is not supported on iOS 17 devices, please see our iOS 17 support documentation.

Prerequisites

Before using the SDK, be mindful that:

  • only iOS 9.0+ is supported due to availability of the API used in the library. iOS 17 is excluded from support.
  • The target device should have at least one real fingerprint ID or face ID registered.
  • The application using this SDK should never be distributed outside the use of HeadSpin's secured devices.

Getting started

Download the <code class="dcode">HSBiometricSDK</code> binary framework containing the biometric SDK from the download section. Follow the steps to link it in your project:

  1. In Xcode, under your project target, under the General tab, in the Embedded Binaries section, drag the <code class="dcode">HSBiometricSDK.xcframework</code> folder from the above download onto Xcode to add the framework and update the header search path.
  2. In the Product menu run "Clean Build Folder" and make sure your build passes.
  3. In your code, try import <code class="dcode">HSBiometricSDK</code> to make sure you can import the module.

HeadSpin Biometric SDK for iOS

If your iOS app is using LAContext for biometric authentication then in your test build replace LAContext with HSLAContext from this SDK.

This will override evaluatePolicy with a version that calls the original but also connects to the HeadSpin UI to allow triggering the callback remotely.

UI integration

When the SDK is used the HeadSpin UI will display a dialog which can be used to trigger biometric authentication in the app.

ios Gf

Sample Apps

Simple Login Sample

This is a very basic sample app which directly uses the result of LAContext.evaluatePolicy to pass a "login" screen. On a HeadSpin device this would not be possible, but with minimal modification of the code to use this SDK the app can be tested.

In this sample we use conditional compilation to maintain two different versions of the app. Only if it is compiled with HEADSPIN_TESTING enabled the HeadSpin biometric SDK is used. In the sample project there are two separate targets, a testing target called "Login (HS Biometrics)" with "-D HEADSPIN_TESTING" added to the Swift compiler options and a target "Login" without the HeadSpin SDK.

The relevant code difference is just the use of <code class="dcode">HSLAContext</code> in place of <code class="dcode">LAContext</code>:


#if HEADSPIN_TESTING
import HSBiometricSDK
#endif

#if HEADSPIN_TESTING
let context = HSLAContext()
#else
let context = LAContext()
#endif

Encryption Sample

This is a slightly more complex sample to demonstrate additional changes that may be required to test an app with biometric authentication.

The HeadSpin SDK does not replace the code of Apple's LAContext - all it does is call the user provided callback, as if authentication succeeded. And it cancels the real biometric authentication dialog on the device. This means a simple authentication check like in the previous example can work but anything that uses actual biometric authentication features will not. Therefore, more code changes are required to bypass such additional features.

The encryption sample uses a secure biometric key to encrypt and decrypt a string. Even if the HeadSpin SDK calls a callback to signal successful authentication this will not unlock the secure keystore on the Apple device. One way to test an app like this sample app is to use an insecure key while in test mode.

To demonstrate the difference between such keys the use of secure biometrics can be changed at runtime in the sample, so in addition to the compile-time HEADSPIN_TESTING the code also has the runtime wantBiometric flag. Because the secure key API can use an internal <code class="dcode"LAContext</code> to authenticate the user we also set an explicit LAContext and manually call .evaluatePolicy on it. This way the "fake" sample where the key can be used without actual biometric authentication still behaves very similar to the real application (using an <code class="dcode">HSLAContext</code> instead).

With these changes if the HS Biometric SDK is included and the biometric requirement for the key is disabled the test app will show a biometric prompt just like the real one did, and you can interact with it from the HeadSpin UI. Relevant code is shown below:


#if HEADSPIN_TESTING
import HSBiometricSDK
#endif

#if HEADSPIN_TESTING
class func getContext() -> HSLAContext {
    return HSLAContext()
}
#else
class func getContext() -> LAContext {
    return LAContext()
}
#endif

// provide a custom LAContext and skip the internal authentication UI
let query: [String : Any] = [
    ...
    kSecUseAuthenticationContext as String: context,
    kSecUseAuthenticationUI as String: kSecUseAuthenticationUISkip
    ...
let r = SecItemCopyMatching(query as CFDictionary, &item)


// turn off biometricCurrentSet if wantBiometric is not set
let accessControl = SecAccessControlCreateWithFlags(
    ...
    SecAccessControlCreateFlags(wantBiometric ? [.biometryCurrentSet] : []),
    ...
let attributes = [
    ...
    kSecPrivateKeyAttrs as String: [
        kSecAttrAccessControl as String: accessControl as Any,
        ...
SecKeyCreateRandomKey(attributes ... )