Skip to main content

Web SDK

The Approov web SDK is provided in a file called approov.js as a module exporting a static Javascript object, called Approov. Through this, any member functions and properties of the SDK can be accessed. The Approov web SDK can be downloaded using the Approov command line tool (see the installation instructions). Use the following command to download the latest web SDK package:

approov sdk -packageID approov.js.zip -getClientPackage approov.js.zip

This writes the latest available web SDK package to the approov.js.zip file (or any path that you specify). Unzip the file and copy the resulting Approov web SDK Javascript file, approov.js, into your project and load it as part of your web app:

Example:

<script type="module" src="/approov.js"></script>
<script type="module">
// Import the Approov SDK
import { Approov, ApproovError, ApproovFetchError, ApproovServiceError,
ApproovSessionError } from '/approov.js'

// Ensure the Approov session is initialized
await Approov.initializeSession({
approovHost: 'web-1.approovr.io',
approovSiteKey: 'your-Approov-site-key',
/* further web protection service specific arguments */
})
// ...

// Call a function of the Approov SDK
Approov.fetchToken('your-Approov-protected-API-domain',
/* further web protection service specific arguments */)
.then(/* do something with the result */)
.catch(/* handle Approov session expiry and any other errors */)
// ...

Domains

The web protections service can be accessed from the following domains:

  • web-1.approovr.com (Primary)
  • web-1.approovr.io (Secondary)

Both of these domains point to the same service instances, running in various regions around the world including the EU and US.

The Secondary domain should be used only in the case of Primary unavailability.

Initialization

initializeSession initializes the Approov Javascript SDK’s single session with the Approov site key and the configuration data for at least one Web Protection Service. An Approov sessions does not last longer than the duration of the current browser session - there is no persistent storage via cookies. Calling initializeSession again reinitializes the session, clearing all state associated with the old session. When calling initializeSession again without arguments, this creates a new session with the same configuration that was used on the previous initialization. initializeSession throws an ApproovError if any required arguments are missing.

static async initializeSession({
approovHost,
approovSiteKey,
fingerprintPublicAPIKey,
hcaptchaSiteKey,
recaptchaSiteKey
})
Parameter NameValue Description
approovHost: stringThe address of the host through which to access the Approov service. One of the options listed in the section Domains.
approovSiteKey: stringSpecifies the Approov site key to identify your Approov account to the web protection servers. Your Approov site key is listed as the first property output from a call to approov web -list.
fingerprintPublicAPIKey: stringSpecifies the Fingerprint public API key for a Fingerprint subscription you have registered with Approov. Optional, but at least one of fingerprintPublicAPIKey, hcaptchaSiteKey, or recaptchaSiteKey must be provided.
hcaptchaSiteKey: stringSpecifies the site key for an hCaptcha site you have registered with Approov. Optional, but at least one of fingerprintPublicAPIKey, hcaptchaSiteKey, or recaptchaSiteKey must be provided.
recaptchaSiteKey: stringSpecifies the site key for a reCAPTCHA site you have registered with Approov. Optional, but at least one of fingerprintPublicAPIKey, hcaptchaSiteKey, or recaptchaSiteKey must be provided.

Example:

// Initialize the session
await Approov.initializeSession({
approovHost: 'web-1.approovr.io',
approovSiteKey: 'your-Approov-site-key',
fingerprintPublicAPIKey: 'your-Fingerprint-public-API-key',
hcaptchaSiteKey: 'your-hCaptcha-site-key',
recaptchaSiteKey: 'your-reCAPTCHA-site-key'
})
// ... some time later, refresh the session, keeping the configuration the same
await Approov.initializeSession({})

Token Fetch

fetchToken fetches a token for the requested API from the Approov service or retrieves a token from the Approov SDK's internal cache if available and returns a Promise for the Approov token. The function throws an error if the SDK is not initialized (ApproovSessionError), a problem occurred during the token fetch (ApproovFetchError), or an Approov token for the requested API is not available. The latter typically indicates that the Approov service has not been configured to provide tokens for the requested API. An ApproovSessionError indicates that the session needs to be (re-)initialized and a new Approov token needs to be requested using a fresh set of web protection service results. An ApproovFetchError is typically caused by an issue in communication with the Approov service - the web app should retry fetching the Approov token. Further details about errors are listed in section Troubleshooting Web Protection Errors.

On the first token fetch following the initialization of an Approov session, at least one of the fingerprintIDResult, hcaptchaToken, or recaptchaToken parameters must be provided in order for the Approov service to perform the relevant web security service lookup. On subsequent calls either the same parameter value can be provided or it can be omitted - in either case the Approov service will not reissue the web security service lookup. If the parameter provided differs from the one passed in the previous call to fetchToken, the Approov service uses this to perform a fresh web security service lookup and then uses the new result from this in any further issued Approov tokens.

static async fetchToken(
api,
{
payload,
fingerprintIDResult,
hcaptchaToken,
recaptchaToken
}
)
Parameter NameValue Description
api: stringSpecifies the domain of the target API. The api parameter must identify an API domain that is registered with your Approov account and has been web enabled. The CLI command, approov api -list will list the registered APIs and their properties, including if they are web enabled.
payload: string | Uint8ArrayAn optional parameter used for token binding on the web channel. The SHA-256 hash of the payload is computed and included as the pay claim of the fetched Approov token. See the section below on Web Protection Token Binding.
fingerprintIDResult: ObjectOptional, an object with the properties fingerprintVisitorId - specifies the visitor ID returned by a Fingerprint identification request and fingerprintRequestId - specifies the request ID returned by a Fingerprint identification request.
hcaptchaToken: string | ObjectOptional, specifies the token returned by an hCaptcha browser lookup. Either a string containing the hCaptcha token or an object containing the hCaptcha token in its response property and, optionally, any of the following properties: key - specifies the challenge key returned by the hcaptcha.getRespKey() API call, and host - specifies the root host domain that must match with the hCaptcha response. See the hCaptcha documentation.
recaptchaToken: stringOptional, specifies the token returned by a reCAPTCHA browser lookup.

Example (using Fingerprint as the web protection service):

// Import the Approov web SDK
import { Approov, ApproovError, ApproovFetchError, ApproovServiceError,
ApproovSessionError } from '/approov.js'
// ...

// Initialize the Approov session and a Fingerprint agent at application startup
const fpPromise = Approov.initializeSession({
approovHost: 'your-Approov-service-domain',
approovSiteKey: 'your-Approov-site-key',
fingerprintPublicAPIKey: 'your-Fingerprint-public-API-key'})
.then(FingerprintJS.load({ token: 'your-Fingerprint-public-API-key' }))
// ...

// When you want to perform a request to your API server
try {
// Try to fetch an Approov token
let approovToken = await Approov.fetchToken('your-Approov-protected-API-domain', {})
// Pass the Approov token with the request to your API server
// ...
} catch (error) {
if (error instanceof ApproovSessionError) {
// If the Approov session has expired, initialize and start a new one
// using the same configuration as in the previous initialization
await Approov.initializeSession({})
// Perform a Fingerprint identification request before the Approov token fetch
let result = await (fpPromise.then(fp => fp.get()))
// Fetch the Approov token with the refreshed session and updated Fingerprint
// identification result
let approovToken = await Approov.fetchToken('your-Approov-protected-API-domain',
{fingerprintIDResult: result})
// Pass the Approov token with the request to your API server
// ...
} else {
// Handle or re-throw other errors
}
}
// Import the Approov web SDK
import { Approov, ApproovError, ApproovFetchError, ApproovServiceError,
ApproovSessionError } from '/approov.js'

// Initialize the Approov session
await Approov.initializeSession({
approovHost: 'web-1.approovr.io',
approovSiteKey: 'your-Approov-site-key',
fingerprintPublicAPIKey: 'your-Fingerprint-public-API-key'
})
// ...

// Initial token fetch of Approov session provides the result of the Fingerprint
// identification request
const approovTokenPromise = Approov.fetchToken(api, {fingerprintIDResult: result})

// Subsequent token fetches during an Approov session don't require the result of
// the Fingerprint identification request
const approovTokenPromise = Approov.fetchToken(api, {})

DPoP

getDPoPToken generates a DPoP (Demonstrating Proof of Possession) JWT (JSON Web Token) using the current key-pair for the active Approov session. Including a DPoP token in a request to your backend API along with the Approov token, allows the backend system to check that the original requester of the Approov token is the same as the one making the request to your backend API. That is, the browser instance in possession of the crypto key that was used for both requests, thus proving that the Approov token is not being reused in a different environment. For more information on why and how to make use of the DPoP token mechanism please refer to DPoP Token Use and Verification.

static async getDPoPToken(method, uri, nonce, token, messageSignature)
Parameter NameValue Description
method: stringthe HTTP method string for the request to be protected by the DPoP token. This is added to the DPoP token as its htm claim.
uri: stringthe HTTPS URI value for the HTTP request to be protected by the DPoP token, ignoring any query and fragment parts. This is added to the DPoP token as its htu claim.
nonce: anya nonce containing a unique (random) value to prevent replay attacks or a falsy value to skip addition of the nonce. Objects and arrays are allowed as well as base types. This is added to the DPoP token as its nonce claim.
token: stringa non-empty string whose hash is to be added to the DPoP token or a falsy value to skip addition of the token hash. The hash is generated using a call to SubtleCrypto.digest using the SHA256 algorithm, as mandated by the DPoP specification. This is added to the DPoP token as its ath claim.
messageSignature: stringa string containing a preconstructed signature or hash for the message. If the message signature is falsy, no message signature is added. The caller is free to use any implementation of their choice to provide the message signature to include in the DPoP token (for example following the Signing HTTP Messages proposal or including a base64URL-encoded SHA-256 hash of the message). This is added to the DPoP token as its msgs claim.

Example:

// Import the Approov web SDK
import { Approov, ApproovError, ApproovFetchError, ApproovServiceError,
ApproovSessionError } from '/approov.js'

// Ensure the Approov session is initialized
await Approov.initializeSession({
approovHost: 'web-1.approovr.io',
approovSiteKey: 'your-Approov-site-key',
/* further web protection service specific arguments */
})
// ...

// Helper function to compute the SHA-256 hash of a string
async function sha256Hash(str) {
const uint8Arr = stringToUint8Array(str)
const hashBuffer = await window.crypto.subtle.digest('SHA-256', uint8Arr);
const result = new Uint8Array(hashBuffer)
return result
}

// Helper function to encode a byte array in base64-url format without padding
function uint8ArrayToBase64URL(uint8Arr) {
// Convert byte array to String (without char interpretation)
const str = String.fromCharCode.apply(null, uint8Arr)
// Convert to base64
const base64 = btoa(str)
// Convert to base64url without padding
const result = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
return result
}

try {
let nonce = new Uint32Array(8);
window.crypto.subtle.getRandomValues(nonce);
let approovToken = await Approov.fetchToken('your-Approov-protected-web-site',
{ /* web protection service request data if required */ })
let messageSignature = uint8ArrayToBase64URL(sha256Hash(requestBody))
let dpopToken = await Approov.getDPoPToken('POST',
'https://' + 'your-Approov-protected-web-site',
uint8ArrayToBase64URL(nonce),
approovToken,
messageSignature)
// Include both the Approov token and the DPoP in the request to your backend API
const response = await fetch('https://' + 'your-Approov-protected-web-site', {
method: 'POST',
headers: { 'Approov-Token': approovToken, 'Dpop': dpop },
body: requestBody
})
} catch (error) {
// Handle Approov session expiry and any other errors
}

Message Signature

messageSignature generates a signature for the message using the current Approov session's key pair and returns a Promise of ArrayBuffer as its result. The signature algorithm and key used are the same as for generating a DPoP token. This provides a means for generating message signatures that can be verified using the public key contained in the DPoP token.

static async messageSignature(message)
Parameter NameValue Description
messagean array (e.g. Uint8Array) or array buffer with the message to sign

Example:

const message = "This is an unmodifiable message"
const signature = await messageSignature(new TextEncoder().encode(message))

Expiries

The Approov SDK provides this facility to report the expiry time of the Approov session and the web protection service results. It enables a web-site to refresh these before they expire and so reduce the chance of an Approov web SDK call to fail because of an expired result and to minimize the impact of expiry events on the user experience.

getExpiries provides the expiry time for the Approov session and the expiry times associated with all web protection service results as a Promise of a map from subscription/site key to the respective expiry times as millisecond time stamps suitable for comparison with the current time as determined by calling Date().getTime(). Example: {"session":1663777376615, "fp:your-Fingerprint-public-API-key":1663780976615, "recap:your-reCAPTCHA-site-key":1663780976615}

static async getExpiries()

This function can be used to determine when web protection service results, or the Approov session, are about to expire and to provide new results or to refresh the Approov session before this happens.

Example - refreshing the Approov session:

// Import the Approov web SDK
import { Approov, ApproovError, ApproovFetchError, ApproovServiceError,
ApproovSessionError } from '/approov.js'

// Ensure the Approov session is initialized
await Approov.initializeSession({
approovHost: 'web-1.approovr.io',
approovSiteKey: 'your-Approov-site-key',
/* further web protection service specific arguments */
})
// ...

const timeLeft = (await Approov.getExpiries()).session - (new Date().getTime())
if (timeLeft < 300000) {
// Renew session if less than 5 minutes left
await Approov.initializeSession({})
// Note that the next Approov token fetch will require fresh web protection
// system results to be provided
}

Example - refreshing the Fingerprint check:

// Import the Approov web SDK
import { Approov, ApproovError, ApproovFetchError, ApproovServiceError,
ApproovSessionError } from '/approov.js'

// Initialize Approov and a Fingerprint agent at application startup.
const fpPromise = Approov.initializeSession({
approovHost: 'your-Approov-service-domain',
approovSiteKey: 'your-Approov-site-key',
fingerprintPublicAPIKey: 'your-Fingerprint-public-API-key'})
.then(FingerprintJS.load({ token: 'your-Fingerprint-public-API-key' }))
// ...

try {
const expiries = await Approov.getExpiries()
const now = new Date().getTime()
for (const property in expiries) {
if (property.startsWith('fp')) {
let aFingerprintTimeLeft = expiries[property] - now
if (aFingerprintTimeLeft < 300000) {
// Refresh Fingerprint identification result if less than 5 minutes left
const fpGetResult = await fpPromise.then(fp => fp.get())
Approov.fetchToken('your-Approov-protected-API-domain',
{fingerprintIDResult: fpGetResult})
}
break
}
}
} catch (error) {
// Handle any errors
}