Warning: This document is for an old version of Approov. The latest version is v1.12.

Android Client API User Guide

Introduction

The Approov SDK provides an API to control the attestation process and retrieve tokens from our servers. The tokens are used to verify the authenticity of the App in customer servers. The SDK is provided as an .aar and can be simply dropped in to an existing Android Studio project.

Importing the Approov SDK into Android Studio

The following instructions are accurate for Android Studio 2.1.2 and above

  1. Make sure the Project View is open (if it is not, click on “1: Project” located on the left hand side near the top).
  2. Depending on your Project View’s “Group Tabs” setting (accessible by right-clicking on “1: Project”), the Project View’s tabs are either visible side-by-side (a), or grouped together in a drop-down menu (b):
  1. select the “Android” tab (at the top of the Project View). If the “Android” tab is not visible, left clicking on the two arrows at the end of the list of tabs brings up a selection of check-boxes. Check the box labeled “Android” to make the “Android” tab visible.
  2. from the Project View’s tab drop-down menu (at the top of the view) select “Android”.
  1. Right-click on the “app” folder in the “Android” tab and select “New” → “Module”.
  2. Select the “Import .JAR/.ARR Package” option and click on “Next”.
  3. Enter the path of your Approov SDK .aar into the “File name” field (or navigate to the .aar and click “OK”). The “Subproject name” field will be populated with the .aar’s name (by default: cb_approov_<client library id>; you can also choose a different sub-project name). The library can be downloaded from the The Approov Admin Portal.
  4. Click “Finish”. The new subproject folder should appear in the “Android” tab.
  5. Right-click on the “app” folder in the “Android” tab and select “Open Module Settings”.
  6. Ensure that the “app” module is selected in the sidebar on the bottom left.
  7. Click on the “Dependencies” tab and click on the green “+” button (labeled “Add”) in the top right corner.
  8. Select “3 Module Dependency”.
  9. Select the Approov SDK module that you just imported (by default: cb_approov_<client library id>), click “OK” and then click “OK” again to close the window.
  10. A Gradle sync will then run.

Once you have successfully completed these steps you are ready to start making use of the Approov SDK.

Note

Before a valid token will be generated by our SDK, the app must be registered (see Android Registration).

Approov API Overview

The Approov SDK provides a simple API for authenticating your app and requesting a time and IP-limited Approov token from the Approov Cloud Service to represent the app’s authenticity which you can then transmit securely to your web services for validation.

The API is centralised around an ApproovAttestation Java class. This class enables you to authenticate your app and fetch Approov tokens either synchronously or asynchronously using the methods fetchApproovTokenAndWait() or fetchApproovToken(TokenInterface callback) depending on your requirements. The TokenInterface is simply an interface provided by Approov which you can implement to handle the fetching of Approov tokens.

An overview of the ApproovAttestation API is shown below:

// ASYNCHRONOUS TOKEN FETCHING:

/**
 * Call to request an attestation token asynchronously. This will return immediately. Once a
 * token has been fetched, the given interface will be called in a new thread.
 *
 * @param pTokenInterface instance of TokenInterface to call once token is obtained
 */
public void fetchApproovToken(TokenInterface pTokenInterface);

// SYNCHRONOUS TOKEN FETCHING:

/**
 * Call to request an attestation token synchronously. This will return the result of fetching
 * the token.
 */
public final TokenInterface.ApproovResults fetchApproovTokenAndWait();

// TOKEN FETCH RESULTS:

// Result code for the fetch token event
public enum AttestationResult {
    SUCCESS, FAILURE;
}

// ASYNCHRONOUS TOKEN FETCH CALLBACK INTERFACE:

// Interface that all callbacks provided to Approov must implement
public interface TokenInterface {

    /** Main working function, to be implemented with relevant business logic */
    void approovTokenFetchResult(ApproovResults pResults);

    /**
     * Plain old data class for returning results
     */
    class ApproovResults {

        /** Approov token string getter
         * @return Approov token string, will be empty string on failure, will not be null
         */
        public String getToken();

        /** Approov token fetch result getter
         * @return Approov token fetch result
         */
        public AttestationResult getResult();
    }
}

Creating An Attestation Object

In order to attest you will first need to create an ApproovAttestation object and an AndroidPlatformSpecifics object. These objects are intended to be long lived and should be created at application start-up so the Approov SDK can start performing attestation before the user attempts to use an attested service. There should only be one instance of the Approov objects that are used throughout your application. We recommend that they are obtained from your application context.

Here is an example of the recommended way to setup your Approov objects. First, either create a new Class that expands the Application class (see the official Android documentation ) or extend your existing one:

public class MyApp extends Application {

    ...

    ApproovAttestation mAttestation;
    AndroidPlatformSpecifics mPlatformSpecifics;

    ...

    @Override
    public void onCreate (){

        ...

        // At app start-up create the Approov objects
        // This will only be done once

        mPlatformSpecifics = new AndroidPlatformSpecifics(this);
        mAttestation = new ApproovAttestation(mPlatformSpecifics);

        ...

    }
}

If you have just created a new Application Class, you also need to add its name to your AndroidManifest.xml:

<application
    ...
    android:name="MyAppName"/>

For each Class that needs to make use of the ApproovAttestation object, it can be obtained from the Application content. For example, an Activity that needs to make attestations:

public class AttestingActivity extends Activity {

    ...

    // Define the Approov objects
    ApproovAttestation mAttestation;
    AndroidPlatformSpecifics mPlatformSpecifics;
    MyApp mMyApp;

    ...

    @Override
    public void onCreate (){

        ...

        if (mMyApp == null){
            mMyApp = (MyApp) getApplication();
        }
        mAttestation = mMyApp.mAttestation;
        mPlatformSpecifics = mMyApp.mPlatformSpecifics;

        ...

    }
}

Note

It is important to note the use of the Application context to create the AndroidPlatformSpecifics object. The use of a short lived context, such as an Activity context, is likely to create memory leaks by holding a reference to the context after the Activity life-cycle has ended.

The TokenInterface interface

The TokenInterface enables Approov to provide a callback mechanism into your code so that you can be notified when an Approov token fetch operation has completed. A concrete class should be created in your code which implements this interface and handles the response from Approov appropriately.

The TokenInterface is shown below:

// Interface that all callbacks provided to Approov must implement
public interface TokenInterface {

    void approovTokenFetchResult(ApproovResults pResults);

    /**
     * Plain old data class for returning results
     */
    class ApproovResults {
        private AttestationResult mEnum;
        private String mToken;

        public ApproovResults(AttestationResult pEnum, String pToken){
            mEnum = pEnum;
            mToken = pToken;
        }

        public String getToken(){
            return mToken;
        }

        public AttestationResult getResult(){
            return mEnum;
        }
    }
}

When using the asynchronous fetchApproovToken API call the object passed in must implement this interface, specifically the approovTokenFetchResult(ApproovResults pResults) method. This method is called in its own thread and is given the results object detailing the results of the latest token fetch attempt. If the attempt was not successful, the token String will be the empty string (it will never be null).

If the token fetch attempt was successful then the token String will contain an Approov token. This token is cached inside the Approov library, as a result it is strongly recommended that the token should not be cached by the app (see Token Lifespan).

Requesting an Approov Token Asynchronously

There are two attestation functions available on the Approov library’s public API, one asynchronous and one synchronous. Here we present the asynchronous method:

mAttestation.fetchApproovToken(aTokenInterface);

This call takes one argument which is an instance of a class implementing the Approov TokenInterface interface (discussed in The TokenInterface interface). The callback function passed in will be called once the token fetch request has been completed and takes as its only argument an ApproovResults object which can be used to obtain the results of the token fetch. Further details of the ApproovResults can be found in The TokenInterface interface. Note that each callback is called in its own thread to ensure that callbacks from multiple places can be served in parallel. It is strongly recommended that the token should not be cached by the app (see Token Lifespan).

Requesting an Approov Token Synchronously

There are two attestation functions available on the Approov library’s public API, one asynchronous and one synchronous. Here we present the synchronous method:

TokenInterface.ApproovResults aApproovResults = mAttestation.fetchApproovTokenAndWait();

This call takes no arguments and upon completion of this blocking call an ApproovResults object is returned which can be used to obtain the results of the token fetch. Further details of the ApproovResults can also be found in The TokenInterface interface. It is strongly recommended that the token should not be cached by the app (see Token Lifespan).

Note

This blocking request may involve network traffic. As a result the request may take some time and should not be called on the UI thread.

Example Asynchronous Token Fetch

...

final Button button = (Button) findViewById(R.id.login);
final TokenInterface callback = new TokenInterfaceImpl();
button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        mAttestation.fetchApproovToken(callback);
    }
});

...

public class TokenInterfaceImpl implements TokenInterface {


    TokenInterfaceImpl() { /* EMPTY */ }

    /**
     * Interface method, the actual callback functionality
     *
     * @param pApproovResults results of fetching approov token
     */
    @Override
    public void approovTokenFetchResult(final ApproovResults pApproovResults) {

        ApproovAttestation.AttestationResult aResult = pApproovResults.getResult();
        String aToken = pApproovResults.getToken();

        if (aResult == ApproovAttestation.AttestationResult.SUCCESS){
            loginFunction(aToken);
        } else if (aResult == ApproovAttestation.AttestationResult.FAILURE){
            displayErrorMessageToUser();
        }
    }
}

final void loginFunction(String pToken){
    /* Logic to put the token into your API login call */
}

Note

The result’s success and failure refer to the Approov library’s attempt to fetch a token and not the validity of the token returned. Failure is generally caused by poor connectivity, resulting in the connection to the Approov server timing out or being unavailable.

Example Synchronous Token Fetch

...

final Button button = (Button) findViewById(R.id.login);
button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        ApproovResults aApproovResults = mAttestation.fetchApproovTokenAndWait();
        if (aApproovResults.getResult() == AttestationResult.SUCCESS) {
            loginFunction(aApproovResults.getToken());
        } else {
            displayErrorMessageToUser();
        }
    }
});

...

final void loginFunction(String pToken){
    /* Logic to put the token into your API login call */
}

Note

The result’s success and failure refer to the Approov library’s attempt to fetch a token and not the validity of the token returned. Failure is generally caused by poor connectivity, resulting in the connection to the Approov server timing out or being unavailable.

Preparing a WebView For Approov

If you are using a WebView to access your server then you can also get your token via JavaScript. To do this you must first ensure that your WebView allows JavaScript. If your WebView does not already make use of JavaScript you will need to enable it:

WebSettings aWebSettings = aWebview.getSettings();
aWebSettings.setJavaScriptEnabled(true);

In order to pass the attestation token to your server inside a WebView you will need to register your application’s WebView with our library. This is done by a simple function call to an AndroidPlatformSpecifics object.

There are two ways of adding a WebView, directly or via the root view. If you know the exact WebView that you want to register then you can directly register your WebView, like this:

WebView aWebview = (WebView) findViewById(R.id.webview);
aPlatformSpecifics.addViewForUpdate(aWebview, mAttestation);

Otherwise, if you do not know the exact WebView you can register the root View. Here is an example:

ViewGroup aRootView = (ViewGroup)aView.getRootView();
aPlatformSpecifics.addViewForUpdate(aRootView, mAttestation);

Note

There is an alternative interface for registering a WebView, aPlatformSpecifics.addViewForUpdate(aWebview). This interface will allow the User Agent String to be updated but will not allow a JavaScript interface to be registered.

Any ViewGroup that is registered with a PlatformSpecifics object will recurse through the view tree to set User-Agent strings and JavaScript interfaces on all WebViews that it encounters. This is why it is possible to register just the root View. It is more efficient to add the WebViews directly as it avoids this search.

Note

The Approov JavaScript API will not be visible until you reload the page. This is automatically done on registration of the WebView.

Note

The Approov JavaScript API is only available for devices running Android 4.2 and above. This is because of a security change that was brought into the Android JavaScript interface at Android 4.2.

Using Approov with your WebView

Once your WebView is set to allow JavaScript and has been registered, all you have to do is add either two or three JavaScript functions to call the Approov SDK’s public API. The first function is the one that requests a token from the Approov API.

This takes either two arguments as shown here:

// Send a request for a token and cope with
// success and failure in separate functions
function requestTokenAndFilter() {
    Approov.fetchApproovToken("fetchSuccess", "fetchFailure");
}

// Callback for success
function fetchSuccess(pToken) {
    // Use new token, pToken
    ...
}

// Callback for failure
function fetchFailure(pToken) {
    // Show error dialogue
    ...
}

Or alternatively, can take one argument:

// Send a request for a token
function requestToken() {
    Approov.fetchApproovToken("callbackFunction");
}

// Callback on success or failure
function callbackFunction(pToken) {
    // if pToken is empty string then was failure
    ...
}

Additionally, it is possible to make a synchronous Javascript request:

// Send a request for a token synchronously
function requestTokenSync() {
    // aToken will either be "" or a string containing an Approov token
    aToken = Approov.fetchApproovTokenAndWait();
    // do work with token
    ...
}

Cleaning Up A WebView

As with any asynchronous use of JavaScript, care must be taken to cleanup properly if transitioning to a different View or Activity. For example, if the Activity with the Approov registered WebView is to login to see some protected content that is dealt with in a different Activity. To do this, the WebView should be de-registered from the Approov SDK and it may be necessary to prevent any further JavaScript from firing on the WebView.

One way to achieve this would be to expand the onStop method of the Activity:

@Override
protected void onStop() {

    ...

    WebView aWebview = (WebView) findViewById(R.id.<WebView ID>);
    mPlatformSpecifics.removeView(aWebview);
    WebSettings aWebSettings = aWebview.getSettings();
    aWebSettings.setJavaScriptEnabled(false);

    ...

}

Using the Approov Java API with the Approov JavaScript API

It is possible to use the Approov Java API and the Approov JavaScript API at the same time. To do so you must follow both the instructions for using the JavaScript API in a WebView and the instructions for using the Java API and implementing the Approov TokenInterface.

Token Lifespan

Approov tokens have a limited lifespan and are cached to avoid unnecessary network traffic. When a call to fetch an Approov token is made, the library checks if the current token is still valid. This allows the call to return a token very quickly in the general case. If the SDK is notified of a network connectivity change this will automatically invalidate the token and cause a new token to be fetched on the next request. In this way the Approov SDK will take care of token expiry behind the scenes.

As a result of this you should not cache the token in any application logic; instead call our SDK to fetched one whenever it is required. This will also help improve security as it minimises the number of copies of the Approov token that are held in memory for any length of time.

Using a VPN

There are known issues with trying to use a VPN connection on Android 4.4 (Kitkat). This may result in the Approov SDK having incorrect information on the connectivity status of the device. This would in turn result in the tokens obtained from the Approov SDK being rejected as invalid by the application servers and prevent the user from being able to use the application. If this behavior is observed the suggested action is to either restart the application or to enable then disable flight mode; either of which should force a connection update.