# DeviceAssure Web Client # ## Table of Contents - [Introduction](#introduction) - [Client to server model](#client-to-server-model) - [Server to server model](#server-to-server-model) - [Sample Application](#sample-application) - [Data](#data) - [Getting Started](#getting-started) - [Client to Server usage](#client-to-server-usage) - [Page Load Setup](#page-load-setup) - [User Event Setup](#user-event-setup) - [Server to Server usage](#server-to-server-usage) - [IMEI Device Check usage](#imei-device-check-usage) - [DeviceAssure Cache Configuration](#deviceassure-cache-configuration) - [Storage Types/Location](#storage-typeslocation) - [Cache Expiry Configuration](#cache-expiry-configuration) - [Troubleshooting](#troubleshooting) - [iFrame Usage](#iframe-usage) - [Alternative Setup (Vue, Angular, React etc)](#alternative-setup-vue-angular-react-etc) <h2 id="introduction">Introduction</h2> The purpose of this JavaScript library is to analyse and validate a device visiting a webpage with the library embedded. The collected data is compared with sets of known valid characteristics for legitimate devices. This comparison and validation is performed by the DeviceAssure backend service. The data collected by the library may be sent to the DeviceAssure service in two ways. <h3 id="client-to-server-model">Client to server model</h3> The data is sent directly from the client library to the DeviceAssure backend service. Validation results are returned to the library and optionally to a configured callback URL. <h3 id="server-to-server-model">Server to server model</h3> The data is collected and made available on the client. The DeviceAssure library will not make a request to the DeviceAssure service. It is the responsibility of the calling application to transmit the data to a suitable server and for that server to make the request to the DeviceAssure service. The validation results are returned directly back to the calling server and not to the client application. --- <h2 id="sample-application">Sample Application</h2> A sample application is included in the package. This sample shows usage for the examples listed. - Client to Server (On Page Load) - Client to Server (On User Event) - Server to Server - IMEI Check A valid DeviceAssure licence key is required for the "Client to Server" and IMEI Check examples. #### Running the Sample Application #### ```bash # Unzip the package and navigate to the SampleApplication directory cd SampleApplication python3 -m http.server 3000 # Now navigate to http://localhost:3000 ``` --- <h2 id="data">Data</h2> The library collects data about the underlying hardware and browser capabilities. It does not collect any PII data. The library stores our data in a configurable location to prevent subsequent calls to the DeviceAssure library for a **30 minute** period. DeviceAssure will not be called again until the cache expires or is deleted. It is possible to delete the cached location within our library. The expiry and the location of the cache can be set, or can be disabled entirely. Please see the DeviceAssure Cache Configuration section for more details. --- <h2 id="getting-started">Getting Started</h2> The DeviceAssure JavaScript library must be included on a webpage for it to work. It is recommended to include the library at the end of the web page. The DeviceAssure library can be used in three different ways: ["Client to Server"](#client-to-server-usage), ["Server to Server"](#server-to-server-usage) and ["IMEI Device Check"](#imei-device-check-usage). There are a number of methods exposed in the DeviceAssure library which should be used depending on the usage scenario required. These are outlined below. | Property | Description | |--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `.check` | Calls the DeviceAssure API. A valid licence is required . Please see the ["User Event Setup"](#user-event-setup) section for more details. | | `.checkIMEI` | Calls the DeviceAssure API and verifies the IMEI supplied is valid for the device. An IMEI and a valid licence are required for this method. Please see the ["IMEI Device Check Setup"](#imei-device-check-usage) section for more details. | | `.getData` | Returns the payload with the dataCollectorKey and serverToServer property turned on. This should only be used for the Server to Server option. Requests are not sent the to the DeviceAssure API when this method is called. Please see the ["Server to Server Setup"](#server-to-server-usage) section for more details. | <h2 id="client-to-server-usage">Client to Server usage</h2> For this option the data is collected by the library and automatically sent to the DeviceAssure API for device verification. There are two recommended ways to use the library in this scenario. The first is ["Page Load Setup"](#page-load-setup) and the second is upon a ["User Event"](#user-event-setup) e.g. a click of a button the `.check` method is called directly. <h3 id="page-load-setup">Page Load Setup</h3> The following is a list of configurations that can be set on the DeviceAssure library for the page load setup. | Property | Type | Default | Description | |------------------|----------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| | `serverToServer` | Boolean | `false` | Enable server to server mode. This will disable the automatic validation to the DeviceAssure service and manual data collection can then occur via `getData`. | | `imei` | Boolean | `false` | Enable IMEI mode. This will disable the automatic validation to the DeviceAssure service and manual calling of the `checkIMEI` method is required. | | `licence` | String | `null` | The licence key to use for the DeviceAssure service. Required for Client to Server usage. | | `onSuccess` | Function | `function() {}` | The callback function to call when the DeviceAssure service returns a successful response. | | `onError` | Function | `function() {}` | The callback function to call when the DeviceAssure service returns an error response. | | `storageType` | String | `local-storage` | The type of storage to use for the DeviceAssure cache. Available options are `local-storage`, `cookie` or `none`. | | `storageExpiry` | Number | `1800000` | The time in milliseconds that the DeviceAssure cache will be valid for. The minimum value is 1 millisecond and the maximum value is 24 hours. | #### Example #### ```html <!-- Setup the configs. --> <script type="text/javascript"> DeviceValidation = { options: { serverToServer: false, imei: false, licence: '<LICENCE_KEY>', storageType: 'cookie', // override 'local-storage' default to 'cookie' onSuccess: function(response) { console.log('Handle successful response here.', response); }, onError: function(error) { console.log('Handle error or failed response here.', error) } } }; </script> <!-- Note how the configs are defined before the import of the DeviceAssure script --> <script src="<path/to/deviceAssure.min.js>" defer></script> ``` A working example can be seen in on the `/index.html` page. --- <h3 id="user-event-setup">User Event Setup</h3> The below sample code shows how to include and how to call the DeviceAssure library when an event is triggered. In the example below, an event is triggered on button press. A valid licence key _must_ be provided by replacing the placeholder `LICENCE_KEY`. **Please Note:** Calling the `.check` method on page load (described above) is ***NOT*** recommended as it could lead to an invalid device detection due to the DeviceAssure library not having enough time to collect all the required data. It is therefore recommended to only call the `.check` method on a user event such as a button click. This can be seen below: ```html <!-- Library must be imported before this is called --> <script src="<path/to/deviceAssure.min.js>"></script> <!-- Call the check method and handle results --> <script type="text/javascript"> var onSuccess = function (response) { console.log('Handle successful response here.', response); }; var onError = function (error) { console.log('Handle error or failed response here.', error) }; $('.example-submit-button').on('click', function() { DeviceValidation.check('LICENCE_KEY', onSuccess, onError); }); </script> ``` The following is a full list of parameters that the `.check` method accepts. With first being `licence` and last being `overridingURLs`. | Parameter | Required | Type | Default | Description | |-------------------|----------|----------|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `licence` | Yes | String | `undefined` | The licence key to use for the DeviceAssure service. | | `onSuccess` | Yes | Function | `function() {}` | The callback function to call when the DeviceAssure service returns a successful response. | | `onError` | Yes | Function | `function() {}` | The callback function to call when the DeviceAssure service returns an error response. | | `payload` | No | Object | `undefined` | An overriding payload that will be sent to the API server. When this is not set, it will build up a default payload and use that. | | `currentUrl` | No | String | `undefined` | The current url the client is pointing to. When this is not set, it will use the first in the urls configured. It will first attempt to use the urls passed in, if none are there, then it will use the defaults | | `overrideLoading` | No | Boolean | `undefined` | A boolean value to help prevent the client from sending a request. | | `overridingURLs` | No | Array | DeviceAssure Cloud API | The urls that the solution will use when set. If nothing is set, the DeviceAssure cloud api will be hit. | **Reference:** ```javascript DeviceValidation.check('licence', onSuccess, onError, payload, currentUrl, overrideLoading, overridingURLs); ``` Please see the Implementation Guide for the structure and examples of the returned validation data. A working example can be seen on the `/on-event.html` page. --- <h2 id="server-to-server-usage">Server to Server usage</h2> In this use-case, the device data is collected by the library and made available to the calling application. It is then the responsibility of the calling application to send the data to an appropriate server that subsequently makes a request to the DeviceAssure service. Please see the Implementation Guide for further details on how to send the data from a server context. Note: A licence key is not required on the client side in this scenario. It must however be provided on the server side before making the request to the DeviceAssure API. The Server to Server functionality may be enabled by adding a **serverToServer: true** parameter to the options object before calling the `.getData` method in the library. This flag will disable the automatic device validation call to the DeviceAssure service and will instead make the collected data available to the calling application. The following is a list of parameters that the `.getData` method accepts. | Parameter | Required | Type | Default | Description | |------------|----------|----------|------------------------|-----------------------------------------------------------------------------------------------------------------| | `callback` | No | function | `undefined` | The method the result will be passed in to. When no argument is given, then the result is immediately returned. | **Please Note:** The serverToServer flag will prevent the automatic trigger of the `.check` method. This method can still be called manually if required and will work as if called in a similar fashion to the Client to Server scenario. The collected data may be retrieved from the library synchronously or asynchronously. Both approaches are shown below. NOTE: Calling the `.check()` method will still work as expected and send a request to the DeviceAssure service. The serverToServer config is only used to prevent the DeviceAssure API call on page load. #### Asynchronously: #### ```html <!-- Enable server to server mode, this will prevent the check request from being sent on page load. --> <script type="text/javascript"> DeviceValidation = { options: { serverToServer: true } }; </script> <!-- Note how the configs are defined before the import of the DeviceAssure script --> <script src="<path/to/deviceAssure.min.js>" type="text/javascript"></script> <!-- call the getData() function --> <script type="text/javascript"> // Asynchronous call by providing a callback function: function callBack(collectedData) { var payload = collectedData; // Proceed with your response or perhaps use our DeviceValidation.check() method above. } // Asynchronous approach DeviceValidation.getData(callBack); </script> ``` #### Synchronously: #### ```html <!-- Enable server to server mode, this will prevent the check request from being sent on page load. --> <script type="text/javascript"> DeviceValidation = { options: { serverToServer: true } }; </script> <!-- Note how the configs are defined before the import of the DeviceAssure script --> <script src="<path/to/deviceAssure.min.js>" type="text/javascript"></script> <!-- call the getData() function --> <script type="text/javascript"> // Synchronous blocking call may be made: var payload = DeviceValidation.getData(); </script> ``` A working example can be seen in on the `/indexServer2Server.html` page. --- <h2 id="imei-device-check-usage">IMEI Device Check usage</h2> The ability to check if a provided IMEI is lost / stolen and aligns with other identifiers is an optional extra. In this scenario, the data is collected on load of the application, much like the server to sever. However, it does not automatically send API calls without the input of a valid IMEI, so this feature is not suited for automatic validation on page load. But best suited for user interaction. #### .checkIMEI Parameters ### The following is a list of parameters that the `.checkIMEI` method accepts. | Parameter | Required | Type | Default | Description | |-------------|----------|--------|-------------|--------------------------------------------------------------------------------------| | `imei` | Yes | String | `undefined` | IMEI value to be tested alongside the deviceassure payload. | | `overrides` | No | Object | `{}` | The overriding values to be used instead of the global configuration values defined. | ##### Overrides definition ##### The overrides object can contain the following properties: | Parameter | Required | Type | Default | Description | |-------------------|----------|----------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `licence` | Yes | String | `null` | The licence key to use for the DeviceAssure service. | | `onSuccess` | Yes | Function | `function() {}` | The callback function to call when the DeviceAssure service returns a successful response. If nothing is passed in, then it will use what is configured in the global configuration, if nothing is defined there, then an empty function will be used. | | `onError` | Yes | Function | `function() {}` | The callback function to call when the DeviceAssure service returns an error response. If nothing is passed in, then it will use what is configured in the global configuration, if nothing is defined there, then an empty function will be used. | | `payload` | No | Object | `null` | An overriding payload that will be sent to the API server. When this is not set, it will build up a default payload and use that. | | `overrideLoading` | No | Boolean | `null` | A boolean value to help prevent the client from sending a request. | #### Please Note: #### A valid licence key is required for use. The IMEI functionality may be enabled by adding the parameter **imei: true** to the options object before calling the library. This flag will disable the automatic validation to the DeviceAssure service and will only send the call request when the `.checkIMEI` method is called on the library. The collected data can be retrieved from the library asynchronously. See below for an example: ##### Callbacks defined in config ##### ```html <script type="text/javascript"> DeviceValidation = { options: { imei: true, //enable the IMEI api call and prevent call on page load licence: '<LICENCE_KEY>', onSuccess: function(response) { console.log('Handle successful response here.', response); }, onError: function(error) { console.log('Handle error or failed response here.', error) } } }; </script> <!-- Note how the configs are defined before the import of the DeviceAssure script --> <script src="<path/to/deviceAssure.min.js>" type="text/javascript"></script> <script type="text/javascript"> $('.example-submit-button').on('click', function() { DeviceValidation.checkIMEI('<IMEI>'); }); </script> ``` ##### Alternative setup ##### ```html <script type="text/javascript"> DeviceValidation = { options: { imei: true, //enable the IMEI api call and prevent call on page load } }; </script> <!-- Note how the configs are defined before the import of the DeviceAssure script --> <script src="<path/to/deviceAssure.min.js>" type="text/javascript"></script> <script type="text/javascript"> var onSuccess = function(response) { console.log('Handle successful response here.', response); }; var onError = function(error) { console.log('Handle error or failed response here.', error) }; $('.example-submit-button').on('click', function() { var overridingConfigs = { licence: '<LICENCE_KEY>', onSuccess: onSuccess, onError: onError, payload: { // getData() payload }, overrideLoading: true }; DeviceValidation.checkIMEI('<IMEI>', overridingConfigs); }); </script> ``` A working example can be seen in on the `/imei.html` page. --- <h2 id="deviceassure-cache-configuration">DeviceAssure Cache Configuration</h2> The DeviceAssure API Response can be stored in the browser inside a cache. This is to prevent subsequent calls to the DeviceAssure API if neccessary. By default the cache is stored in local-storage. It is possible to modify the default behaviour in order to store this information in another type of web-storage. <h3 id="storage-typeslocation">Storage Types/Location</h3> The available options are: #### local-storage #### Using `local-storage` (default behaviour) means DeviceAssure will use localStorage to track whether to call the check endpoint. It stores a JSON string with the time of expiry and the blocking value. #### cookie #### Using `cookie` means DeviceAssure will use cookies to track whether to call the check endpoint. The expiration of the cookie will indicate whether to make another call or not. #### none #### Using `none` will never save any response from DeviceAssure. This means that every time the DeviceAssure library is called, it will make a call to the DeviceAssure service. It will also wipe any keys from the cache that were previously added. Please be aware, this could be an expensive option as it will make a call every time the library is loaded. This option is not recommended. If any of these storage options are cleared or expire, then another DeviceAssure call is made. <h3 id="cache-expiry-configuration">Cache Expiry Configuration</h3> It is also possible to set the expiry time of the cache. This is the time in milliseconds that the cache will be valid for. The default value is 30 minutes. It is not necessary to specify the storageExpiry if the default is used. Once the cache expires, another DeviceAssure API call will be made. The valid configuration for this property is between 1 millisecond and 24 hours. This is to prevent some unwanted/persisted behaviour. #### Examples: ### ##### Configuring storageType and Cache Expiry on page load ##### ```html <!-- Setup our configs. --> <script type="text/javascript"> window.DeviceValidation = { options: { licence: 'LICENCE_KEY', onSuccess: function(response) { // success callback is optional console.log('Handle successful response here.', response); }, onFailure: function(error) { // failure callback is optional console.log('Handle error or failed response here.', error) }, storageType: 'cookie' // cache type to store test results - available options 'cookie', 'local-storage' or 'none. Default is 'local-storage'. storageExpiry: 8 * 60 * 60 * 1000 // cache expiry time in milliseconds. Default is 30 minutes. } }; </script> <!-- Note how the configs are defined before the import of the DeviceAssure script --> <script src="<path/to/deviceAssure.min.js>" defer></script> ``` ##### Configuring storageType and Cache Expiry on user event #### ```html <!-- The storageType can be specified in the config --> <script type="application/javascript"> DeviceValidation = { options: { storageType: 'none' // cache type to store test results - available options 'cookie', 'local-storage' or 'none storageExpiry: 8 * 60 * 60 * 1000 // cache expiry time in milliseconds. Default is 30 minutes. } }; </script> <!-- Library should be imported before this is called --> <script src="<path/to/deviceAssure.min.js>"></script> <!-- Call the check method and handle results --> <script type="text/javascript"> var onSuccess = function (response) { console.log('Handle successful response here.', response); }; var onError = function (error) { console.log('Handle error or failed response here.', error) }; $('.example-submit-button').on('click', function() { DeviceValidation.check('LICENCE_KEY', onSuccess, onError); }); </script> ``` A more in depth example can be seen in the example page found in example/index.html. --- <h2 id="troubleshooting">Troubleshooting</h2> <h3 id="iframe-usage">iFrame Usage</h3> If the DeviceValidation library is embedded in a cross-origin iframe, the following attributes are required to surpress warnings in the console. The library will continue to work, the errors/warnings are non-blocking in the library. `allow="accelerometer; gyroscope;"` ##### Warning logs ``` [Violation] Permissions policy violation: accelerometer is not allowed in this document. [Violation] Permissions policy violation: gyroscope is not allowed in this document. ``` ##### Full Example: ```html <iframe id="deviceassure-iframe" src="https://path-to-cross-origin-page>" allow="accelerometer; gyroscope;" > </iframe> ``` --- <h2 id="alternative-setup-vue-angular-react-etc">Alternative Setup (Vue, Angular, React etc)</h2> Alternatively, the DeviceValidation library can be declared on the window object. (This can be useful when implementing in a framework such as AngularJS, React or Vue). ### window.DeviceValidation ### ```html <!-- Setup our configs. --> <script type="text/javascript"> window.DeviceValidation = { options: { licence: 'LICENCE_KEY', onSuccess: function(response) { // success callback is optional console.log('Handle successful response here.', response); }, onFailure: function(error) { // failure callback is optional console.log('Handle error or failed response here.', error) } } }; </script> <!-- Note how the configs are defined before the import of the DeviceAssure script --> <script src="<path/to/deviceAssure.min.js>" defer></script> ``` ### Javascript usage for Vue, Angular, React etc ### The library can to be imported into a js file dynamically and then setup at runtime inside a component. Angular and React will need to do something similar too with loading the library in at runtime. A Vue example component can be seen below: ```vue <template> <div class="deviceassure-component"></div> </template> <script> export default { name: "DeviceAssure", props: { licence: { type: String, default: '<LICENCE_KEY>', }, imei: { type: Boolean, default: false, }, serverToServer: { type: Boolean, default: false, }, onSuccess: { type: Function, default: () => {}, }, onError: { type: Function, default: () => {}, }, // other optional configs should be setup here (cache type, expiry time etc). }, created() { this.loadDeviceAssure(); }, methods: { loadDeviceAssure() { if (window.DeviceValidation && window.DeviceValidation.check) { this.instance = window.DeviceValidation; return; } // Normal setup here window.DeviceValidation = { options: { licence: this.$props.licence, imei: this.$props.imei, serverToServer: this.$props.serverToServer, onSuccess: (data) => { this.response = data; this.$props.onSuccess(data); console.log("onSuccess: ", data); }, onError: (err) => { this.error = err; this.$props.onError(err); console.log("Error: ", err); }, }, }; import("@/libs/dv.min.js").then(() => { if (!this.instance) { this.instance = window.DeviceValidation; console.log("Loaded DeviceAssure", this.instance); } }); }, checkIMEI(imei) { var overridingConfigs = { onSuccess: (data) => { console.log("Check IMEI onSuccess: ", data); }, onError: (err) => { console.log("Check IMEI Error: ", err); }, }; this.instance.checkIMEI(imei, overridingConfigs); }, check() { this.instance.check( this.$props.licence, (data) => { this.response = data; this.$props.onSuccess(data); }, (err) => { this.error = err; this.$props.onError(err); } ); }, getResponse() { return this.response; }, getError() { return this.error; }, }, data() { return { instance: null, response: {}, error: {}, }; } } </script> ```` --- ## Copyright ## Copyright (c) DeviceAtlas Limited 2024. All rights reserved. https://deviceatlas.com