Android - Device Intelligence Integration Guide

Device Fingerprinting helps you understand your user’s unique hardware with their device data. It efficiently syncs the data in the background, processes them and the data processed can be used to generate a fingerprint id.

Integration Overview

  • Integrate Android SDK in your application
  • Call the API to query device information
Device Intelligence Flow

Device Intelligence Flow

  1. SessionKey and UserID is generated from client backend and sent to the SDK.
  2. Invoke the submit function in SDK to pass the data to Bureau backend.
  3. Upon successful transmission of the parameters, a callback is received in the SDK, based on the callback (Success/Failure) next steps can be taken.
  4. If the callback is successful, Invoke Bureau's backend API /v1/suppliers/device-fingerprint to fetch the results.
  5. Use the fingerprint and risk signals to allow, block or redirect the user to a desired flow.

Usage

The SDK is initialised in the client app and once the submit function is called the data relating to the user and device are automatically synced in the background.

Minimum Requirements

  • minSdkVersion 21
  • AndroidX

Getting Started

Add following lines in your root build.gradle

Properties properties  = new Properties()
properties.load(new FileInputStream(project.rootProject.file('local.properties')))
buildscript {
.....
}
allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io"}
    }
}

Add following lines in your module level build.gradle. The latest dependency can be obtained from here

dependencies {
//add the following dependency in your gradle file
implementation 'com.github.Bureau-Inc:prism-android-fingerprint-sdk:0.00.0'}

This library also uses some common android libraries. So if you are not already using them then make sure you add these libraries to your module level build.gradle

  • androidx.appcompat:appcompat:1.2.0

Initialise SDK

//Get SDK Instance with Client Id and Environment
val bureauAPI : BureauAPI  = BureauInstanceProvider.getBureauApiInstance(context,YOUR_CLIENT_ID,
                ENV_SANDBOX or ENV_PRODUCTION)

            bureauAPI.setSessionId(YOUR_SESSION_ID)
            
            bureauAPI.setFlow(YOUR_FLOW_NAME)//optional
            
            bureauAPI.setUserId(YOUR_USER_ID)

//Start collecting and submit data to Bureau's backend using the submit function
            bureauAPI.submit(object :
                DataCallback {
                override fun onError(errorMessage: ErrorResponse) {
                  
                }

                override fun onResult(message: SubmitResponse) {
                  
                }
            })
//Get SDK Instance with Client Id and Environment
BureauAPI bureauAPI = BureauInstanceProvider.getBureauApiInstance(context, "YOUR_CLIENT_ID", Environment.ENV_PRODUCTION);
bureauAPI.setSessionId("YOUR_SESSION_ID");  //Mandatory
bureauAPI.setFlow("your_flow_name");  //optional
bureauAPI.setUserId("your_user_id");  //optional


//Start collecting and submit data to Bureau's backend using the submit function
bureauAPI.submit((new DataCallback() {
   public void onError(@NotNull ErrorResponse errorMessage) {
       //implement your own logic
   }

   public void onResult(@NotNull SubmitResponse message) {
       //implement your own logic
   }
}));

Note:

  • To initialise the SDK we need to provide CREDENTIAL_ID.
  • Session Id is mandatory and unique for each submission.
  • To obtain the fingerprint Id query Bureau backend.
  • The default environment is production. If you want to run on UAT pass ENV_SANDBOX in getBureauApiInstance function.

Response returned from the SDK

The DataCallback added in the Submit function returns whether the device data has been registered or not.

object :DataCallback{
            override fun onError(errorMessage: ErrorResponse) {
               //Failure Callback
               Log.w(TAG,"Error "+errorMessage)
            }

            override fun onResult(message: SubmitResponse) {
                //Success Callback
                Log.w(TAG,"Success "+message)
            }
        }}
bureauAPI.submit((new DataCallback() {
   public void onError(@NotNull ErrorResponse errorMessage) {
       //implement your own logic
   }

   public void onResult(@NotNull SubmitResponse message) {
       //implement your own logic
   }
}));

Backend API Integration

Backend URL's

Authentication

API's are authenticated via an clientID and secret, they have to be base64 encoded and sent in the header with the parameter name as Authorisation.

Authorisation : Base64(clientID:secret)

Request

curl --location --request POST 'https://api.overwatch.stg.bureau.id/v1/suppliers/device-fingerprint' \
--header 'Authorization: Basic MzNiNxxxx2ItZGU2M==' \
--header 'Content-Type: application/json' \
--data-raw '{
    "sessionKey": "697bb2d6-1111-1111-1111-548d6a809360"
}'

curl --location --request POST 'https://api.overwatch.bureau.id/v1/suppliers/device-fingerprint' \
--header 'Authorization: Basic MzNiNxxxx2ItZGU2M==' \
--header 'Content-Type: application/json' \
--data-raw '{
    "sessionKey": "697bb2d6-1111-1111-1111-548d6a809360"
}'

Response

{
                "GPSLocation": {
                    "city": "Ahmedabad",
                    "country": "IN",
                    "latitude": 23.03499984741211,
                    "longitude": 72.56400299072266,
                    "region": "Gujarat"
                },
                "IP": "43.242.116.163",
                "IPLocation": {
                    "city": "Ahmedabad",
                    "country": "IN",
                    "latitude": 23.03499984741211,
                    "longitude": 72.56400299072266,
                    "region": "Gujarat"
                },
                "IPSecurity": {
                    "VPN": false,
                    "isCrawler": false,
                    "isProxy": false,
                    "isTor": false,
                    "threatLevel": "low"
                },
                "IPType": "v4",
                "OS": "android",
                "VPN": null,
                "conditionsEvaluationStatus": true,
                "debuggable": true,
                "emulator": true,
                "error": null,
                "fingerprint": "ce53c2cd-xxxx-40c3-xxxx-41400751a07e",
                "firstSeenDays": 11,
                "isAppCloned": false,
                "isAppTampered": false,
                "mockgps": false,
                "model": "sdk_gphone64_arm64",
                "package": "com.rxxxo.app.staging",
                "riskLevel": "HIGH",
                "rooted": false,
                "status": "OK",
                "totalUniqueUserId": 3,
                "userId": "9xxxx6x4"
            }
        }
{
  "data": null,
  "errors": {
    "status": 401,
    "errorCode": "UNAUTHORIZED",
    "service": "Overwatch"
  },
  "message": "",
  "meta": {
    "length": 0,
    "took": 0,
    "total": 0
  }
}
{
  "error": {
    "code": 0,
    "description": "",
    "message": "Server encountered an error",
    "metadata": null,
    "referenceId": "86529a18-a5cb-4da9-91b0-8d04cdb9167e",
    "type": "INTERNAL_SERVER_ERROR"
  },
  "merchantId": "auth0|61dfxxxx0071be7021",
  "requestId": "c69d86f0-xxxx-4ef0-xxxx-e687d595a507",
  "statusCode": 500,
  "timestamp": 1657009043753
}
{
    "error": {
        "code": 422,
        "description": "Failed to find fingerprint for given session key",
        "message": "NO_RECORD_FOUND",
        "metadata": null,
        "referenceId": "",
        "type": "NO_RECORD_FOUND"
    },
    "merchantId": "auth0|61dfbbxxxx420071be7021",
    "requestId": "24e1aa7f-xxxx-404d-xxxx-5f8a0227e8f0",
    "statusCode": 422,
    "timestamp": 1658402132141
}
{
    "statusCode": 400,
    "error": {
        "code": 0,
        "type": "BAD_REQUEST",
        "message": "Session key is missing",
        "description": "request does not contain additionalData.sessionKey param in request",
        "referenceId": "24f94ae8-xxxx-48a4-xxxx-b25f99fb06d9",
        "metadata": null
    },
    "timestamp": 1658402143450,
    "merchantId": "auth0|61dfbbxxxx3420071be7021",
    "requestId": "66403193-xxxx-44bc-xxxx-14735a45dfeb"
}

Response Description

KeyDescription
GPSLocation(city, country, latitude, longitude, region)GPS Based location of the user, user's consent is required to be taken to get this details.
IPLocation(city, country, latitude, longitude, region)IP based location of the user.
IPIP address of the user.
VPNFlag to indicate if the IP being used by the user is a VPN. (Note the VPN within the IPSecurity JSON object should be used and not the one outside.
isCrawlerFlag to indicate if the IP being used by the user is associated with a crawler.
isProxyFlag to indicate if the IP being used by the user is a proxy.
isTorFlag to indicate if the IP being used by the user is of a Tor network.
threatLevelThe threat level associated with the IP. Possible values can be ["low", "medium", "high"]
IPTypeThe type of IP, ["v4", "v6"]
OSThe OS of the user's device.
debuggableFlag to indicate if the app is in debug mode.
emulatorFlag to indicate if the app is being run on an emulator
fingerprintA hash generated for the device, this identifier will be unique for a device.
firstSeenDaysThe number of days from which the device is identified on Bureau's network.
isAppClonedFlag to indicate if the app is cloned.
isAppTamperedFlag to indicate if the app is tampered.
mockgpsFlag to indicate if the user is spoofing their GPS location. This will need users permission for background location.
modelThe device model.
packageThe package name.
riskLevelRisk level of the user, calculated based on the flags that are exposed in the above sections. Possible values ["LOW", "MEDIUM", "HIGH"]
rootedFlag to indicate if the device is rooted.

Go live Checklist

  1. For determining the app tamper check, we would require your package name and signature hash code, kindly ensure this is shared before going live. The hash code can be obtained from PackageManager GET_SIGNING_CERTIFICATES signingInfo.signingCertificateHistory this will be a byte array.