iOS SDK User Guide

This document provides a developer-related overview of the iOS Approov framework used by the Approov service to authenticate iOS apps. The framework can be integrated into an existing app by using the framework’s public API for greater customizability.

Overview

App authentication is achieved by the following process:

  1. The framework authenticates itself and its host app in memory at runtime.
  2. This authentication result is challenged and verified remotely by the Approov cloud service.
  3. The Approov cloud service provides the framework with a unique, cryptographically-signed, time-limited Approov Token whose validity is only known by the Approov cloud service and your servers which hold the “token secret”.
  4. The Approov Token representing the app’s authenticity is securely transmitted from the app to your servers and validated.

The Approov service is made available to iOS apps by means of an embedded dynamic framework, that is, a bundle which must be embedded into an existing app and configured to load dynamically at runtime. The framework contains a universal dynamic library with device and simulator architectures, however the app authentication process is only valid when running on an actual device.

Requests to fetch new Approov Tokens can be made through the framework’s public API which generates notifications when the Approov Token is available. These notifications can be observed by your app and the appropriate step taken to provide the Approov Token securely to your servers. There is also a synchronous version of the API if that works better with your app.

Every effort has been made to ensure the Approov framework does not impact user experience and the app’s performance will always take precedence over the framework.

Compatibility

OS Version

The Approov framework is compatible with all iOS 8.0 and above devices which have not been jail-broken. There is currently no support for tvOS, watchOS, and OSX/macOS.

Third-Party Frameworks

The Approov framework has been tested for compatibility with the following third-party app frameworks:

  • Adobe PhoneGap
  • Apache Cordova
  • Cocos2d
  • Microsoft Xamarin
  • Unity

Apple App Store Review Process

The Approov framework has been tested for compatibility with the Apple app store review process. It should be noted, however, that this process is not always consistent between apps or reviews and it is ultimately your responsibility to ensure any issues with the app review are addressed. Always consult Apple’s App Store Review Guidelines online document prior to submitting your app for review.

Network Communication

A secure wifi or cellular network connection supporting Transport Layer Security (TLS) version 1.2 is required to communicate with the Approov cloud service. Both IPv4 and IPv6 IP addresses are supported.

Bitcode

Bitcode, sometimes referred to as LLVM Intermediate Representation (IR), is not currently supported by Approov.

Adding the Framework to an App

The Approov framework can be added to an existing app Xcode project using the following steps:

  1. In the Xcode project editor, select the target to which you want to add the Approov framework.
  2. Select the “General” tab.
  3. Select the + (plus) icon under the “Embedded Binaries” section.
  4. Select “Add Other...” in the file dialog and browse to the Approov.framework bundle location.
  5. Select “Open” in the file dialog.
  6. Ensure the “Copy items if needed” destination option is checked, then select Finish.

The Approov.framework entry should now be present in the “Embedded Binaries” and “Linked Frameworks and Libraries” sections indicating that this will be included and linked with your app.

To access the Approov framework in source code, the public header must be imported. This can be achieved by adding the following to the top of your source or header files:

Examples

iOS - Swift
import Approov
iOS - Objective-C
#import <Approov/Approov.h>

Fetching an Approov Token in Swift or Objective-C

All interaction with the Approov framework in source code is achieved by accessing the shared singleton ApproovAttestee instance.

The framework will invoke callbacks that capture Approov Token fetch results when Approov Tokens are fetched or if the operation fails for some reason (for example, no internet access).

You must always provide the hostname for the endpoint you are about to access with the token you are fetching. The hostname can be in any RFC 2396 URL compatible format.

You should decide carefully the appropriate places in your app to call the fetchApproovToken(hostname) method on the ApproovAttestee instance. The fetchApproovToken(hostname) method will operate over a secure network link. In order to operate efficiently, this method will only retrieve an Approov Token from the remote Approov cloud service if necessary.

The framework maintains a cache of the previously-fetched Approov Token and will re-issue that locally in your app if there is still a sufficient valid lifetime for the token to be issued to and validated by your servers. This is done to ensure multiple calls to fetchApproovToken(hostname) do not result in multiple unnecessary network transactions which are power intensive.

If the framework detects that a network change has occurred (for example, a switch from wifi to cellular or vice versa) then a new Approov Token will be fetched from the remote cloud service since the app device’s IP address may have changed which would invalidate the current Approov Token.

Calls from your app to fetchApproovToken(hostname) do not directly return the Approov Token. Instead, an asynchronous request is made in a separate low-overhead queue and fetchApproovToken(hostname) returns immediately. Some time later, the framework will invoke a given callback that the Approov Token is available or the operation failed (typically due to network access issues). The time between calling fetchApproovToken(hostname) and the callback that a token is available will vary depending on whether a previous valid Approov Token has been cached internally and still has some usable lifetime, or if the Approov framework decides a new token must be remotely fetched from the Approov cloud service.

The ApproovTokenFetchData interface is used for the callback of the asynchronous fetch Approov token method. Its parameters ApproovTokenFetchResult and NSString will contain the following important entries:

  1. The values of ApproovTokenFetchResult enum (defined in the framework’s public header) are “Successful” and “Failed”.
  2. The value of NSString is the fetched Approov Token.

Initiating a request to asynchronously fetch an Approov token is achieved in the following manner:

Examples

iOS - Swift
let attestee = ApproovAttestee.shared()
attestee?.fetchApproovToken({ (tokenFetchData: ApproovTokenFetchData) in
    switch tokenFetchData.result {
        case .successful:
            // The token is available in 'tokenFetchData.approovToken'
        case .failed:
            // We failed to fetch a token
    }
}, "api.myservice.io")
iOS - Objective-C
ApproovAttestee *attestee = [ApproovAttestee sharedAttestee];
[attestee fetchApproovToken:^(ApproovTokenFetchData * tokenFetchData) {
    switch (tokenFetchData.result) {
        case ApproovTokenFetchResultSuccessful:
            // The token is available in 'tokenFetchData.approovToken'
            break;
        case ApproovTokenFetchResultFailed:
            // We failed to fetch a token
            break;
    }
} :@"api.myservice.io"];

The Approov token can then be transmitted to your web services for validation. There are various methods of achieving this depending on your requirements, but a typical example would be to include the token as an additional HTTP header.

Note

Approov tokens are valid only for a finite period of time and it is therefore very important that these are not cached locally. They should always be retrieved through the fetchApproovToken(hostname) or fetchApproovTokenAndWait(hostname) methods.

Warning

The Approov SDK securely delivers the Token to your app but it is your responsibility to ensure the token is securely transmitted to your API. The connection between app and API must use HTTPS and be Pinned to your API host to prevent theft of valid tokens.

You can use the Approov SDK to implement Dynamic Pinning with MITM Detection if you do not already have Certificate Pinning in place. This avoids the operational issues associated with deploying Certificate Pinning while providing a similar level of protection and will prevent theft of valid tokens.

Synchronous fetch Approov token operation

The Approov framework provides synchronous fetch Approov token operation for legacy support. However, the asynchronous method, which is described earlier, with callback block/closure should be used as the preferred approach for responsive mobile apps and this is the preferred iOS approach.

Synchronous requests to perform an attestation can be made by calling fetchApproovTokenAndWait(hostname) method.

Note

Note that it is not recommended to call fetchApproovTokenAndWait(hostname) on the main/UI thread which could block the UI for a number of seconds and negatively affect user experience.

The ApproovTokenFetchData interface is used for the return value of the synchronous fetch Approov token method. Its parameters ApproovTokenFetchResult and NSString will contain the following important entries:

  1. The values of ApproovTokenFetchResult enum (defined in the framework’s public header) are “Successful” and “Failed”.
  2. The value of NSString is the fetched Approov Token.

Initiating a request to synchronously fetch an Approov token is achieved in the following manner:

Examples

iOS - Swift
if let tokenFetchData = ApproovAttestee.shared()?.fetchApproovTokenAndWait("api.myservice.io") {
    switch tokenFetchData.result {
        case .successful:
            // The token is available in 'tokenFetchData.approovToken'
        case .failed:
            // We failed to fetch a token
    }
}
iOS - Objective-C
ApproovTokenFetchData *tokenFetchData = [[ApproovAttestee sharedAttestee] fetchApproovTokenAndWait:@"api.myservice.io"];
if (tokenFetchData) {
    switch (tokenFetchData.result) {
        case ApproovTokenFetchResultSuccessful:
            // The token is available in 'tokenFetchData.approovToken'
            break;
        case ApproovTokenFetchResultFailed:
            // We failed to fetch a token
            break;
    }
}

Fetching an Approov Token in JavaScript

The Approov framework supports JavaScript in both UIWebView and WKWebView web views using a common interface. Individual web view instances can be registered and unregistered with Approov to support the fetching of Approov tokens directly from within JavaScript.

The first step is to register a specific web view with the Approov framework:

Examples

iOS - Swift
if let attestee = ApproovAttestee.shared() {

    // register a UIWebView or WKWebView with Approov
    let success = attestee.register(webView)
}
iOS - Objective-C
ApproovAttestee *attestee = [ApproovAttestee sharedAttestee];

// register a UIWebView with Approov
BOOL success = [attestee registerUIWebView:webView];

// register a WKWebView with Approov
BOOL success = [attestee registerWKWebView:webView];

Once a web view is registered with Approov, it is now possible to fetch an Approov token from within JavaScript running in the web view. This begins an asynchronous operation which, when done, will invoke a specified JavaScript callback function depending on the outcome of the fetch token operation. An example is shown below:

Examples

JavaScript
// fetch an Approov token asynchronously
approov.fetchApproovToken("mySuccessCallback", "myFailureCallback", "api.myservice.io");

function mySuccessCallback(token) {
    // do something with the Approov token
}

function myFailureCallback() {
    // handle the failure
}

Note that the same JavaScript code works both for UIWebView and WKWebView.

It is also possible to fetch an Approov token synchronously within Javascript running in a UIWebView. This will block the function call until an Approov token is available (or a timeout occurs) and return the Approov token on success or empty string on failure. Note that this synchronous function is not available for WKWebView due to the asynchronicity of the Javascript/native message-passing bridge. An example is shown below:

Examples

JavaScript
// fetch an Approov token synchronously
var token = approov.fetchApproovTokenAndWait("api.myservice.io");

Once you have finished with Approov access via a web view, for example before dismissing the web view’s UIViewController, it is important to allow Approov to properly clean up allocated resources by unregistering the web view with the Approov framework:

Examples

iOS - Swift
if let attestee = ApproovAttestee.shared() {

    // unregister a UIWebView with Approov
    attestee.unregisterUIWebView(webView)

    // unregister a WKWebView with Approov
    attestee.unregisterWKWebView(webView)
}
iOS - Objective-C
ApproovAttestee *attestee = [ApproovAttestee sharedAttestee];

// unregister a UIWebView with Approov
[attestee unregisterUIWebView:webView];

// unregister a WKWebView with Approov
[attestee unregisterWKWebView:webView];

Once a web view is unregistered, it will no longer be possible to fetch Approov tokens within the web view’s JavaScript code. Web views may be registered and unregistered with Approov multiple times as long as these are paired, i.e. web views should not be registered multiple times without unregistering them first.

Including a custom claim

In order to extend the security of the Approov token through a strength in depth approach that ties a particular Approov token to an arbitrary string it is possible to include a hash of arbitrary data as one of the Approov token’s claims. This is intended to be used for long lived data, such as an OAuth token or a user session id, that can be used to uniquely identify a user. The data is supplied to the Approov SDK as an ASCII encoded string. The SDK takes the string and puts the bytes through a SHA-256 hash and then base64 encodes the result. Details of how the claim appears in the token can be found in the server side documentation, see Customer Payload Data for more details.

The setTokenPayloadValue(value) function should be called immediately after startup to ensure that a value is always set so that there is no call to fetch an Approov token before this value is set. The value of payload can be changed at any time, but doing so will cause a new token to be fetched, rather than returned a cache token, the next time a call to fetch an Approov token is made. As such it is recommended that this value should not be changed often. In the case of wanting to fetch an Approov token when no suitable data is available (such as before the user is logged in), it is necessary to call setTokenPayloadValue(...) with a sentinel value that should not be a valid value that will be used in later operation. Values such as the empty String or “NONE” are likely to be suitable.

Note

The use of this Custom Payload feature is applicable for either the synchronous fetchApproovToken(...) or asynchronous fetchApproovTokenAndWait(...) methods, both in Swift and Objective-C.

Examples

iOS - Swift
ApproovAttestee.shared()?.setTokenPayloadValue(value)
iOS - Objective-C
[[ApproovAttestee sharedAttestee] setTokenPayloadValue:value];
iOS - JavaScript
approov.setTokenPayloadValue(value);

Troubleshooting: Enabling Approov logging

It is possible to increase the log output of the Approov framework in order to facilitate troubleshooting and obtain technical assistance when contacting CriticalBlue for support. During application start-up, Approov checks for the presence of “CA3.logconfig” file in the Documents folder. If the “CA3.logconfig” file is detected, logging level of the Approov library is set according to the contents of the file. The “CA3.logconfig” is a text file and may contain one and only one of the following strings:

  • LOG_ERROR
  • LOG_WARN
  • LOG_INFO
  • LOG_DEBUG

The default log configuration is LOG_ERROR and even if “CA3.logconfig” is not present in the Documents folder, Approov will log the error messages as long as the application supports iTunes file sharing. In order to add the “CA3.logconfig” to the Documents folder of an application or access the log file produced by Approov, the property label “Application supports iTunes file sharing” has to be added to the Information property list in Xcode and the Boolean value has to be set to “Yes”. If modifying the Information property list with an external editor, the equivalent property key is “UIFileSharingEnabled” and the value must be set to “true”. It is then possible to use iTunes or Apple Configurator to add and retrieve files to the Documents folder of an application. If using iTunes to access the Documents folder of an application, please remember to synchronize the device for the changes to take effect.