Skip to main content

Fetching Approov Tokens

This section describes the methods that should be used to obtain an Approov token from the SDK. This is the token that is required to pass to a subsequent backend API call.

warning

You should never cache an Approov token in your app code. Always make a call to fetch a token immediately prior to making an API request that needs it. The Approov SDK automatically caches the Approov token and only performs a network request if a new one is required. Moreover, the SDK performs additional fast checks on app integrity every time an Approov token is fetched.

warning

Once your app is able to fetch Approov tokens, we strongly recommend that you also implement dynamic pinning. As well as protecting your users' data, this will also protect Approov tokens from being stolen with a Man-in-the-Middle attack.

Synchronous Token Fetching

This describes how to fetch an Approov token with the synchronous form of the call. This method does not return until a token has been fetched. If a cached token may be used, then the call will return promptly. If a new token is required, then the call will include the time to complete the network requests with the Approov cloud service and so there may be some delay before returning. The exact delay will depend on many factors, especially network connectivity quality.

warning

You must never make this call directly from the main or UI thread of your application. This is a potentially long running blocking call, and there may be unpredictable delays and effects on your app if it blocks UI processing. Use an asynchronous call instead.

The code to make the call is very simple, as follows:

Approov.TokenFetchResult approovResult = Approov.fetchApproovTokenAndWait("https://api.myservice.io/endpoint");

The method takes a single parameter specifying the API domain for which a token is being fetched. This must be one that has been configured to provide Approov tokens or else an error will result. Check the fetch status portion of the returned result and react appropriately:

if (approovResult.getStatus() == Approov.TokenFetchStatus.SUCCESS) {
String token = approovResult.getToken();
// proceed with the API request, adding the Approov token to the request
} else if ((approovResult.getStatus() == Approov.TokenFetchStatus.UNPROTECTED_URL) ||
(approovResult.getStatus() == Approov.TokenFetchStatus.UNKNOWN_URL) ||
(approovResult.getStatus() == Approov.TokenFetchStatus.NO_APPROOV_SERVICE)) {
// proceed with the API request, but without adding an Approov token to the request
} else if ((approovResult.getStatus() == Approov.TokenFetchStatus.NO_NETWORK) ||
(approovResult.getStatus() == Approov.TokenFetchStatus.POOR_NETWORK) ||
(approovResult.getStatus() == Approov.TokenFetchStatus.MITM_DETECTED)) (
// network conditions do not allow the token fetch so allow a user initiated retry
} else {
// unexpected error handling
}

Asynchronous Token Fetching

An asynchronous token fetching approach is also provided. This accepts a callback method parameter alongside the target domain. The callback is called either when an Approov token is available or there is an error and is always called from a different thread to the one that requests the callback. The fetchApproovToken method allows token fetches to be initiated from threads that cannot be blocked, such as UI threads. When invoked, the callback must check the fetch status of the provided result and react appropriately:

class ApproovCallbackHandler implements Approov.TokenFetchCallback {
@Override
public void approovCallback(Approov.TokenFetchResult pResult) {
switch (pResult.getStatus()) {
case Approov.TokenFetchStatus.SUCCESS:
String token = pResult.getToken();
// proceed with the API request, adding the Approov token to the request
case Approov.TokenFetchStatus.UNPROTECTED_URL:
case Approov.TokenFetchStatus.UNKNOWN_URL:
case Approov.TokenFetchStatus.NO_APPROOV_SERVICE:
// proceed with the API request, but without adding an Approov token to the request
case Approov.TokenFetchStatus.NO_NETWORK:
case Approov.TokenFetchStatus.POOR_NETWORK:
case Approov.TokenFetchStatus.MITM_DETECTED:
// network conditions do not allow the token fetch so allow a user initiated retry
default:
// unexpected error handling
}
}
}
ApproovCallbackHandler approovCallback = new ApproovCallbackHandler();
Approov.fetchApproovToken(approovCallback, "https://api.myservice.io/endpoint");

Getting an Attestation Response Code

The Attestation Response Code is provided that is available once an Approov token is fetched. This is available even if the Approov token is a JWE and thus encrypted. Use the following call:

String arc = approovResult.getARC();

If the Approov fetch was unsuccessful, or the ARC capability is not enabled, then an empty string is returned. Otherwise the short base32 encoded ARC string is provided. It is safe to include this in logging, since without access to the Approov backend it is not possible to decode its contents.

Getting the Rejection Reasons

The Rejection Reasons are provided that is available once an Approov token is fetched. Use the following to access it:

String reasons = approovResult.getRejectionReasons();

If the fetch was unsuccessful, or resulted in a pass result, then an empty string is provided. Otherwise a comma separated list of device properties causing a rejection are provided.

This information may be presented to the user as an explanation of why they are unable to proceed in the app.

You can rely on the device property names not changing, so you may wish to split the string and check against individual commonly occurring properties (e.g. rooted or jailbroken) and provide a more detailed explanation to the user.

info

This rejection reasons capability is only available in version 3.0.0 and later SDKs.