Approov Web Protection Integration

Web Protection Integration

If you have a single API which accepts requests from both the web and mobile channels, then you may appreciate the ability to have shared code for granting access to protected data. The Approov flow has been extended with a facility for web-protection-requests to enable this. The server-side flow of several, best in class, browser solutions have been integrated into Approov to communicate their results back to browsers wrapped in an untamperable Approov token. At a minimum, this enables a single simple token check in the API backend to authorize access. However, if required, Approov can be configured to embed the full results of the integrated protection to allow for deeper interrogation.

There is support for the following web protection services: FingerprintJS, Google reCAPTCHA, and hCaptcha.

  • FingerprintJS fingerprintjs.com: FingerprintJS provides a powerful mechanism for fingerprinting the browser environment of a user and deriving a visitor ID based on both the raw fingerprint and the history of prior visits for that user. This visitor ID can then be compared against a user identity in the backend system to determine if they match. If they do, then there is a high probability that it is indeed the correct user and operations can proceed. If not, then this may indicate an attempt at account takeover or simply that the user has moved to a different browser environment. In either case, additional verification steps should be introduced into the flow to protect the user’s account.
  • Google reCAPTCHA developers.google.com/recaptcha: Google reCAPTCHA is a popular service for determining if a browser is being operated by a human. A browser first retrieves a token from the reCAPTCHA API which is then passed to the protected API as part of a request. A query, from the protected API, obtains the full set of results associated with the token and uses this to determine whether to accept or reject its request.
  • hCaptcha hcaptcha.com: hCaptcha is another popular service for determining if a browser is being operated by a human. To get started, it provides a solution that is a near drop-in replacement for Google reCAPTCHA, however, it also provides further configuration options which extend the capabilities and/or customize behavior. For an overview of the enterprise level features, you should check out botstop.com.

To use an integration, you must signup for the associated service but then follow modified instructions for using those services described in the associated section under Implementing the Integrated Services Flow.

Just because you can doesn’t mean you should. The primary Approov service can provide a very strong indication that a request originates from an untampered instance of your app running on a user device. The integrated web services are not attempting to solve this issue directly and therefore cannot provide the same level of confidence, even if combining multiple integrations. (Even without a specific attack, the built in debug facilities of browsers make it too easy for users to copy tokens, or other data, that a service provider would prefer to be kept private.) As such, we recommend that you restrict critical API endpoints to only work from the Mobile App. The solution presented here, supports this: after successfully verifying a token signature, you can always look at the token claims to determine if it was issued for the web or mobile channel and use that to inform your access control logic.

In some cases, it may be necessary to have a more involved access check. For example, the mobile app token includes a did (DeviceID) claim that uniquely identifies the particular install of an app. If required, the protected API can associate this ID with a user’s account. Such an association can then be checked on each API request to identify those requests that don’t match and then highlight them as having a heightened risk of fraud. The mismatch can be used to trigger additional security steps that determine if the difference is caused by an attempt to misuse the credentials from another valid app instance, or because the legitimate account owner is logging in from a different mobile device.

In the web-flow we can achieve the same kind of check by embedding the results of a FingerprintJS lookup in the Approov token. This exposes the FingerprintJS visitorID to the API backend, enabling the same kind of association as the DeviceID and a corresponding security flow when a new visitorID is identified for a user. A similar binding is also possible using the user identifiers available to enterprise level hCaptcha customers. In a similar vein, the Google reCAPTCHA or hCaptcha services can be used to provide confidence that requests have not been scripted.

Web Protection Flow

The combined Mobile and Web flows are shown in the following diagram:

Web Protection Flow

The left side of the diagram illustrates the primary Approov mobile app protection flow:

  • 1 The mobile app is integrated with the Approov SDK and registered with the Approov service.
  • 2 & 3 When the app is to make a call to a protected backend API, it first fetches an Approov token from the Approov SDK. The SDK performs a complex integrity measurement process to prove that the app is genuine and running in an uncompromised environment. This operation is performed in conjunction with the Approov cloud service. If the app passes the various tests then it is provided with a short lived Approov token.
  • 4 The Approov token is sent as part of the API request to the protected backend API.
  • 5 The backend API includes an Approov token check. Any requests not including a valid Approov token are rejected.

The right side of the diagram shows the parallel web protection flow, with one or more integrations in place and using a common backend API:

  • At least one of the supported services must be integrated into the web application, FingerprintJS, Google reCAPTCHA, or hCaptcha. Each of these requires a service specific account to be set up which is subject to their own pricing terms.
  • 1 The Approov CLI is used to register the integrated protection subscriptions/sites with the Approov backend and to perform other configuration.
  • The agent code for the integrated protection(s) is added to the web site by following the instructions for the associated service.
  • The web-side of the integrated protection check should be initiated prior to an API call by following the associated docs.
  • 2 To retrieve an Approov token, a web protection call is made to the Approov backend with the results from the integrated protection call and other arguments to control the generation of the Approov token (an Approov account site key, the target API and an optional token binding argument as supported by the app-flow). Note that it is possible to combine multiple integrated protections, for example FingerprintJS with Google reCAPTCHA or hCaptcha. In that case, all associated parameters are passed in a single web protection request.
  • 3 The web protection request causes a lookup to be made from the Approov servers to the integrated protection servers, using the provided parameters.
  • A correctly signed (very) short-lived Approov token is only returned if all configured integrated protection services return valid responses. The Approov token will include a claim indicating that it was issued for the web-flow and, by default, a minimal set of properties from the integrated protection response. Approov can be configured to include the full integrated protection response, however this requires that the generated Approov tokens be encrypted as some values in the responses are sensitive. An invalid Approov token will be returned if any of the checks for the integrated protections fail. (An error response is also possible.)
  • 4 & 5 Lastly, the Approov token is sent as part of the API request to the protected backend API in the same way as it is in the mobile app and with the same backend verification step being used to grant or deny access.

Implementing the Integrated Service Flow

The following steps are required to setup a web protection service.

  1. Modify the check in one or more of your Approov protected backend APIs to discriminate between mobile attestations and web protection requests
  2. Use the Approov CLI to enable web protection for those modified APIs
  3. Sign up for your chosen supported 3rd party service
  4. Modify your web app to call out to the selected service
  5. Configure your Approov account to use the service
  6. Insert the call to Approov, passing in the results from the selected service call
  7. Pass the resulting token to your protected API
  8. View activity and billing data in your metrics portal

The first two steps, dealing with server side changes, are covered in the next section. Steps 3 through 7 are specific to the integrated service and the sections, FingerprintJS, Google reCAPTCHA, and hCaptcha take you through those steps for each of the associated services. The section on Web Protection Metrics Presentation covers step 8 and completes the basic flow. A final section on web protection provides a reference for the service endpoints and also covers more advanced topics: using multiple integrated services, obtaining tokens for multiple API calls in one request, adding token binding to your web app, and troubleshooting.

Enable Web Protection for an API

The first step is to audit the target API(s) to decide which endpoints or queries will service both web and mobile requests and those that will be unique to one or the other. As mentioned previously, web protections do not provide the same level of confidence as the Approov mobile API protection service, that a request has not been constructed by a threat actor. The table below shows the claims that can be used to distinguish between a token issued to a mobile app and one issued to a web app:

ClaimDescription
did The did claim is present in all tokens obtained from the mobile app attestation channel. It identifies the unique ID for attesting device. Testing for it's presence is the easiest way to identify tokens originating from this channel whether they come from the Primary service or from the Failover.
embed The embed claim is present in all tokens obtained from the web protection channel. It holds a JSON object whose keys indicate the specific web protection service, or services, that were used to obtain the token and whose values are brief summaries, or the full results, obtained from the wrapped services. If it is important to query the results obtained from the 3rd party service in your backend, then you can also use the presence of the claim itself to determine which channel created the token. Note that, because the full web protection results may contain entries that must be kept private in order to maintain solution security, you must use encrypted tokens (JWEs) to include it in the generated Approov token.

Note that, in either case you should first ensure that the token you have received is valid, signed in the expected way with the correct secret. See the section on Backend integrations.

If you have an existing mobile only API that you are considering opening up to the web channel, then it is imperitave that you first perform some kind of end-point audit to identify those API entries that could be enabled for web-access, those that shouldn’t, and those that mustn’t. As mentioned previously, Approov mobile API protection provides a strong degree of confidence that requests originate from legitimate installs of your apps. This confidence cannot be reproduced for the web channel.

Use the Approov CLI to enable the web-flow for your APIs. The section on adding APIs with the Approov CLI covers all the options available when adding an API. Web protection is included in that documentation, however, in short, web protection requests are enabled for an API when it is added with the -allowWeb flag. If you wish to enable web protection for an existing API definition you just add it again using the same parameters as before but also including the -allowWeb flag. To disable web protection, you add the API again but without the flag. Each addition of the same API, completely overwrites the previous settings for that API.

$ approov api -add your.domain -allowWeb
WARNING: adding the API will have an immediate impact on your apps in production.
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain your.domain with type:account, alg:HS256, pin:JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=
enabled continuous monitoring for https://your.domain:443 (remove using `approov monitoring -removeAPI`)

To receive a token at all, all web protection requests must include at least one web-enabled target API. So, if you are adding your first integrated service it may be useful to first add a web-enabled development API domain that you can include in your test requests.

Note that, if you intend to embed the full results from the integrated protection server-side lookup, then you must also use encrypted tokens for the API to prevent leaking potentially sensitive data from the integrated protection. Only APIs configured to use encrypted tokens will get the full results embedded in the token. An API will use an encrypted token if it is configured to use a JWK with an encryption algorithm, or, if you use the -jwe flag, then the default encryption secret for your account will be used to encrypt tokens with the A256GCMKW algorithm:

$ approov api -add your.domain -allowWeb -jwe
WARNING: adding the API will have an immediate impact on your apps in production.
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain your.domain with type:account, alg:HS256, pin:JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=
enabled continuous monitoring for https://your.domain:443 (remove using `approov monitoring -removeAPI`)

You must change your back-end server to accept the new token type before changing the token signing algorithm for your active APIs, including the addition of the -jwe flag. Ideally, the server should be altered to accept both the new and old token type while the new token format propagates through the flow.

Several sections provide details on handling APIs, secrets, and encryption: Adding API Domains, Managing Keysets, JWE Token Encryption, and Account Secret Key Export.

FingerprintJS

FingerprintJS provides a powerful mechanism for fingerprinting the browser environment of a user and deriving a visitor ID based on both the raw fingerprint and the history of prior visits for that user. This visitor ID can then be compared against a user identity in the backend system to determine if they match. If they do, then there is a high probability that it is indeed the correct user and operations can proceed. If not, then this may indicate an attempt at account takeover or simply that the user has moved to a different browser environment. In either case, additional verification steps should be introduced into the flow to protect the user’s account.

FingerprintJS Signup and Setup

Before proceeding with an integration you must signup for an account: FingerprintJS Signup. Each FingerprintJS account can have one or more subscriptions and each subscription needs a browser token and an API token to work. You should follow the most appropriate JavaScript agent documentation to the point where you obtain an fp.get() result.

Importantly, for best results from the FingerprintJS service, you should follow the instructions for adding a subdomain to handle FingerprintJS queries: Subdomain setup.

Configure Approov with a FingerprintJS Subscription

A FingerprintJS subscription can be added by specifying the subscription region and by providing a valid Browser and API token. You can add multiple subscriptions: the browser token is a public key to identify the target subscription for a web protection request. The following command would add a subscription in the Rest-of-the-World(RoW) region leaving all other settings at their default values:

$ approov web -fingerprintjs -add <FingerprintJS-BrowserToken> -apiToken <FingerprintJS-APIToken> -region RoW
WARNING: changing the web configuration will have an immediate impact in production
ATTENTION: If you wish to continue then please type YES and return: YES
fingerprintjs subscription <FingerprintJS-BorwserToken> added

Listing all the configured web integration properties shows that the new subscription has been added:

$ approov web -list
Site Key: <Account-Site-Key>
Token Lifetime: 120 seconds
FingerprintJS:
  Optional: true
  Subscription Key: <FingerprintJS-BrowserToken>
    Region: RoW
    Max Elapsed Time: 2.00s
    Max Bot Probability: 1.00
    Embed Result: false

The following table lists properties in order and provides their description:

Property Name Description
Site Key This is the Approov site key you need to use to identify your Approov account in web protection API requests
Token Lifetime This is the lifetime given to Approov tokens obtained from web protection. It may be changed using approov web -setTokenLifetime <seconds>.
FingerprintJS The heading that separates FingerprintJS specific properties from general properties and those of other integrations.
Optional Indicates whether a FingerprintJS result must be provided for every web protection request or not. This defaults to true which allows a valid Approov token to be provided for a web protection using another configured integration even if a FingerprintJS result is not provided. This property may be unset using approov web -fingerprintjs -setRequired and reset using approov web -fingerprintjs -setOptional.
Subscription Key Displays the <FingerprintJS-BrowserToken> used to configure the subscription and serves as the heading for properties related to just this subscription. Note that the <FingerprintJS-APIToken> is not listed; the API token is the private key for the subscription and it is never displayed or logged by Approov. If you use the FingerprintJS dashboard to change the API Token for a subscription, then you can always update the Approov subscription by adding it again with the new API token.
Region The configured region for the subscription, either RoW or EU. This must match the region specified for the subscription in the FingerprintJS dashboard.
Max Elapsed Time Specifies the maximum permitted time between a FingerprintJS get call and the subsequent Approov web protection request. The 2 second default matches the delay enforced by the Google reCAPTCHA and hCaptcha flows. To change this value, use the -maxElapsedTime <duration> parameter when you add the subscription.
Max Bot Probability One of the values returned from a FingerprintJS server-side lookup is a BotProbability score. Approov will return invalid tokens if the FingerprintJS lookup returns a score higher than the configured max. The default, 1.0, will accept all scores. To change this value, use the -maxBotProbability <score> parameter when you add the subscription.
Embed Result If this is true *and you have chosen to use encrypted tokens*, JWEs, for the target API then the full FingerprintJS result object will be included in generated Approov tokens. It defaults to false, use -embedResult when you add a subscription to turn it on.
Rate Limit This is not in the above list because no rate limit was specified, but it may be configured when you add a subscription to specify the maximum number of requests per minute: -rateLimit <limit>. Attempts to make requests beyond the limit will result in an error. The limit is specified in terms of the maximum number of requests per minute. If this is not specified, or set to 0, then no limit is imposed.

To change a subscription you simply add it again with all the properties required for the changed subscription. Each addition of the same browser token completely overwrites the previously stored entry.

Call Approov Web Protection with FingerprintJS

Once the subscription is setup in Approov (and you’ve waited a couple minutes or so for the configuration to propagate), you can try retrieving an Approov token using a web protection request end-point. A simple async function to perform the request is provided below:

  async function fetchApproovToken(visitorId, requestId) {
    const params = new URLSearchParams()
    params.append('approov-site-key', '<Approov-Site-Key>')
    params.append('api', '<Target-Approov-Protected-API-Domain>')
    params.append('fingerprintjs-token', '<FingerprintJS-Browser-Token>')
    params.append('fingerprintjs-visitor-id', visitorId)
    params.append('fingerprintjs-request-id', requestId)

    const response = await fetch('https://web-1.approovr.io/attest', {
      method: 'POST',
      body: params
    })
    if (!response.ok) {
      throw new Error(response) // reject with a throw on failure
    }
    return response.text() // return the token on success
  }

With a small modification the example code provided by FingerprintJS can be adapted to call this function:

Excerpt from Original FingerprintJS Integration

// Initialize an agent at application startup.
const fpPromise = FingerprintJS.load({ token: 'your-browser-token' })

// Get the visitor identifier when you need it.
fpPromise
  .then(fp => fp.get())
  .then(result => console.log(result.visitorId))

FingerprintJS/Approov Integration

// Initialize an agent at application startup.
const fpPromise = FingerprintJS.load({ token: 'your-browser-token' })

// Get the visitor identifier when you need it.
fpPromise
  .then(fp => fp.get())
  .then(result => fetchApproovToken(result.visitorId, result.requestId))
  .then(token => {
    // perform the API call adding the Approov token
  })
  .catch(err => console.log('Failed FingerprintJS/Approov promise chain: ' + err))

Note that the Approov token should never be logged in production code.

Approov embed token claim for FingerprintJS

You may wish to interrogate the FingerprintJS results in your backend. FingerprintJS properties are added to the embed claim of the Approov token which is a JSON object with a property called fpjs:<BrowserToken>, where the BrowserToken is the FingerprintJS Browser Token registered to your account and used in the Approov token request. By default only the visitorId and requestId are included in the token (using the same JSON structure as the full FingerprintJS result). The following example shows the full decoded body of an Approov token with the default properties embedded from a FingerprintJS request lookup:

{
  "exp": 1620146455,
  "ip": "1.2.3.4",
  "arc": "LL3BRGIELU",
  "embed": {
    "fpjs:wdATWfQIsdYwATBYYIch": {
      "visitorId": "eZsCHxhztqEOX0ZmOlwi",
      "visits": [
        {
          "requestId": "lsodLAU4ubccLnMzrI3N"
        }
      ]
    }
  }
}

If the -embedResult flag is used when you register the FingerprintJS subscription and you have chosen to use encrypted tokens, JWEs, for the target API then the full results are included in the token. A slightly abbreviated example is shown below:

{
  "exp": 1620128085,
  "ip": "1.2.3.4",
  "arc": "EAM2W37PSU",
  "embed": {
    "fpjs:wdATWfQIsdYwATBYYIch": {
      "visitorId": "eZsCHxhztqEOX0ZmOlwi",
      "visits": [
        {
          "requestId": "Otc6rGJcax7PrUuby7GA",
          "browserDetails": {
            "browserName": "Chrome",
            "browserMajorVersion": "90",
            "browserFullVersion": "90.0.4430",
            "os": "Linux",
            "device": "Other",
            "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36",
            "botProbability": 0
          },
          "incognito": false,
          "ip": "1.2.3.4",
          "ipLocation": { "<IP-keys...>": "<IP-values...>" },
          "time": "2021-05-04T11:32:30Z",
          "timestamp": 1620127950391,
          "url": "<URL-of-the-calling-webpage>",
          "tag": {}
        }
      ]
    }
  }
}

You should now be setup to generate and receive web protection Approov tokens using FingerprintJS. For the full reference on the web protection API endpoints and the more advanced features that are provided, please see the reference section after the integration summaries.

Google reCAPTCHA

Google reCAPTCHA is a popular service to determine if a browser is being operated by a human. A browser request retrieves a token from the reCAPTCHA service which is then passed to the protected API as part of a request to be processed. A second request from the API backend obtains the full set of results associated with the token and uses this to determine whether to accept or reject the original request.

reCAPTCHA Signup and Setup

Before proceeding with an integration you must signup for a key-pair; there’s a link from the developer guide introduction: Google reCAPTCHA. The Approov integration supports v2(tickbox or badge) and v3. Each key-pair has a site-key and a secret which you will use to register the site with Approov. Continue to follow the developer guide for your selected reCAPTCHA flow; the instructions below will show how to modify the flow to add an Approov web protection request.

Configure Approov with a reCAPTCHA Site

A site can be added to Approov by providing the reCAPTCHA site-key and the associated secret. You can add multiple sites: the site-key is the unique key that identifies the target site for a web protection request. The following command adds a site to your Approov account leaving all other settings at their default values:

$ approov web -recaptcha -add <reCAPTCHA-Site-Key> -secret <reCAPTCHA-Secret>
WARNING: changing the web configuration will have an immediate impact in production
ATTENTION: If you wish to continue then please type YES and return: YES
recaptcha site <reCAPTCHA-Site-Key> added

Listing all the configured web protection properties shows that the new site has been added:

$ approov web -list
Site Key: <Account-Site-Key>
Token Lifetime: 120 seconds
reCAPTCHA:
  Optional: true
  Site Key: <reCAPTCHA-Site-Key>
    Min Score: 0.00
    Include IP: false
    Embed Result: true

The following table lists properties in order and provides a description:

Property Name Description
Site Key This is the Approov site key you need to use to identify your Approov account in web protection API requests
Token Lifetime This is the lifetime given to Approov tokens obtained from a web protection request. It may be changed using approov web -setTokenLifetime <seconds>.
reCAPTCHA The heading that separates reCAPTCHA specific properties from general properties and those of other integrations.
Optional Indicates whether a reCAPTCHA result must be provided for every web protection request or not. This defaults to true which allows a valid Approov token to be provided for a web protection request using another configured integration even if a reCAPTCHA result is not provided. This property may be unset using approov web -recaptcha -setRequired and reset using approov web -recaptcha -setOptional.
Site Key Displays the <reCAPTCHA-Site-Key> used to configure the site and serves as the heading for properties related to just this site. Note that the <reCAPTCHA-Secret> is not listed; the secret is the private key for the site and it is never displayed or logged by Approov.
Domains This lists the domains registered against the site; the entry is omitted if no domains were specified. Multiple domains can be registered by using the -domain flag multiple times. The backend reCAPTCHA lookup performed by Approov returns the domain that performed the reCAPTCHA. If one or more domains are defined, then the returned domain must be found in this list. If no domains are registered, then a domain check is not performed. This facility can be used to selectively enable some of the domains that you have registered with the reCAPTCHA site in Google.
Actions This is not in the above list because no actions were specified as the site was added but the entry lists the actions registered against the site using the -action <value> argument. Multiple actions may be registered by using the -action flag multiple times. The backend reCAPTCHA lookup performed by Approov may return the action that performed the reCAPTCHA(v3 only); if the list of actions registered with Approov is non-empty, then the returned action string must be in the list for Approov to return a valid token. If no actions are registered, or if no action is provided by the backend reCAPTCHA lookup, then the check is not performed.
Min Score The Score returned from a reCAPTCHA server-side lookup represents the probability that the website user is a human. Approov will return invalid tokens if the reCAPTCHA lookup returns a score lower than the configured min. The default, 0, will accept all scores. To change this value, use the -minScore <score> argument when you add a site.
Embed Result If this is true *and you have chosen to use encrypted tokens*, JWEs, for the target API then the full reCAPTCHA result object will be included in generated Approov tokens. It defaults to false, use -embedResult when you add a site to turn it on.
Rate Limit This is not in the above list because no rate limit was specified, but it may be configured when you add a site to specify the maximum number of requests per minute: -rateLimit <limit>. Attempts to make requests beyond the limit will result in an error. The limit is specified in terms of the maximum number of requests per minute. If this is not specified, or set to 0, then no limit is imposed.

To change a site’s properties you simply add it again with all the properties required for the change. Each addition of the same site-key completely overwrites the previously stored entry.

Call Approov Web Protection with reCAPTCHA

Once the site is setup in Approov (and you’ve waited a couple minutes or so for the configuration to propagate), you can try retrieving an Approov token using a web protection request end-point. A simple async function to perform the request is provided below:

  async function fetchApproovToken(recapToken) {
    const params = new URLSearchParams()
    params.append('approov-site-key', '<Approov-Site-Key>')
    params.append('api', '<Target-Approov-Protected-API-Domain>')
    params.append('recaptcha-site-key', '<reCAPTCHA-Site-Key>')
    params.append('recaptcha-token', recapToken)

    const response = await fetch('https://web-1.approovr.io/attest', {
      method: 'POST',
      body: params
    })
    if (!response.ok) {
      throw new Error(response) // reject with a throw on failure
    }
    return response.text() // return the token on success
  }

The Approov token should never be logged in production code.

The precise code required to obtain the reCAPTCHA token and then call the above function will depend on the reCAPTCHA version and mechanism that you have selected. In each case, after obtaining the reCAPTCHA token, you next pass it to fetchApproovToken before passing the resulting Approov token to your backend. The discussion below focuses on changes to the Google docs for reCAPTCHA v2 Checkbox style, however, the transformations are applicable to any of the reCAPTCHA flows.

The simplest example provided for reCAPTCHA v2 Checkbox style, Automatically render reCAPTCHA example, is to introduce a div with the g-recaptcha class inside the form in which the reCAPTHCA challenge will be issued. In this approach, the HTML in the docs doesn’t retrieve the reCAPTCHA token that results from the challenge, instead it is embedded in the form data and submitted from there. To use Approov in the same style, you will need to ensure that the Approov token is also added to a hidden form input field before the form is submitted. One approach for this, dervied from Google example, is provided below:

<html>
  <head>
    <title>reCAPTCHA demo: Simple page</title>
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
  </head>
  <body>
    <script>
      async function fetchApproovToken(recapToken) {
        // as above...
      }
      function dataCallback(recapToken) {
        fetchApproovToken(recapToken)
          .then(approovToken => {
            const tokenEl = document.getElementById('approovToken')
            tokenEl.value = approovToken
            enableSubmitCheck()
          })
      }
      function enableSubmitCheck() {
        // a function triggered whenever we need to check if the submit button
        // should be enabled. Note that this is important, so that we can check
        // for the presence of the Approov token before the form is submitted.
        let shouldDisable = false
        // Other checks here ...
        const tokenEl = document.getElementById('approovToken')
        if (tokenEl.value == "") {
          shouldDisable = true
        }
        const submitEl = document.getElementById('submit')
        if (shouldDisable) {
          submitEl.setAttribute('disabled', true)
        } else {
          submitEl.removeAttribute('disabled')
        }
      }
    </script>
    <form action="?" method="POST">
      <div class="g-recaptcha" data-sitekey="<reCAPTCHA-Site-Key>" data-callback="dataCallback"></div>
      <br/>
      <input id="approovToken" type="hidden" value="" name="approovToken"></input>
      <input id="submit" type="submit" value="Submit" disabled>
    </form>
  </body>
</html>

The downside of this approach is that a user action is required (clicking the submit button) between the event that retrieves the approovToken and the form submission. This means that the default Approov token lifetime is likely to be too short. Extending the token lifetime is not recommended unless network latency is causing token exipiry before it can be processed by your backend API. The preferred solution is to override the form’s default submit behavior so that the Approov token fetch can be inserted in that flow. This second approach requires that your javascript code construct and issue the request to your API, however, this is also best practice because you can then adjust the request to precisely match the one used by your mbile app (which is highly likely to include some properties in request headers, like the Approov token).

A modified version of the above, may look something like the example below:

<html>
  <head>
    <title>reCAPTCHA demo: Simple page</title>
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
  </head>
  <body>
    <script>
      async function fetchApproovToken(recapToken) {
        // as above...
      }
      function submitForm() {
        const recapToken = grecaptcha.getResponse()
        if (recapToken != "") {
          fetchApproovToken(recapToken)
            .then(approovToken => {
              // construct fetch request for API call ...
            })
            .catch(err => {
              // handle token fetch error
            })
        }
      }
    </script>
    <form action="javascript:submitForm()">
      <div class="g-recaptcha" data-sitekey="<reCAPTCHA-Site-Key>"></div>
      <br/>
      <input id="submit" type="submit" value="Submit">
    </form>
  </body>
</html>

Other examples provided in the reCAPTCHA documentation can also be adapted in a similar vein to the one described.

Approov embed token claim for reCAPTCHA

You may wish to interrogate the reCAPTCHA results in your backend. Google reCAPTCHA properties are added to the embed claim of the Approov token which is a JSON object with a property called recap:<SiteKey>, where the SiteKey is the reCAPTCHA site-key registered to your Approov account and used in the Approov token request. By default only the challenge timestamp and the domain properties are included in the token (using the same JSON structure as the full reCAPTCHA result), thus confirming that a reCAPTCHA lookup occurred. The following example shows the full decoded body of an Approov token with the default properties embedded from a reCAPTCHA request lookup:

{
  "exp": 1620398492,
  "ip": "1.2.3.4",
  "arc": "EDZ2BABJIO",
  "embed": {
    "recap:6Ldz3WgaAAAAAA7wWZEgs1J_hZabCrBjgZ5Wz3YM": {
      "challenge_ts": "2021-05-07T14:39:15",
      "hostname": "<domain-of-the-calling-webpage>"
    }
  }
}

If the -embedResult flag is used when you register the Google reCAPTCHA site and you have chosen to use encrypted tokens, JWEs, for the target API then the full results are included in the token. Note that the results from the Google reCAPTCHA lookup vary depending on the version. A slightly abbreviated example is shown below:

{
  "exp": 1620653328,
  "ip": "1.2.3.4",
  "arc": "IACJXOWMRO",
  "embed": {
    "recap:6Ldz3WgaAAAAAA7wWZEgs1J_hZabCrBjgZ5Wz3YM": {
      "success": true,
      "challenge_ts": "2021-05-10T13:26:30Z",
      "hostname": "<domain-of-the-calling-webpage>"
    }
  }
}

You should now be all setup to generate and receive web protection Approov tokens using Google reCAPTCHA. For the full reference on the web protection API endpoints and the more advanced features that are provided, please see the reference section after the integration summaries.

hCaptcha

hCaptcha is a popular service to determine if a browser is being operated by a human. A browser request retrieves a token from the hCaptcha service which is then passed to the protected API as part of a request to be processed. A second request from the API backend obtains the full set of results associated with the token and uses them to determine whether to accept or reject the original request.

hCaptcha Signup and Setup

Before proceeding with an integration you must signup for an account: hCaptcha signup. Each hCaptcha account has a secret and it can have one or more sites with each site having a unique site-key. You should follow the Developer guide to either insert the hCaptcha widget, or use the invisible captcha approach. In either case, the instructions below will show how to add Approov web protection to the flow.

Configure Approov with an hCaptcha Site

An hCaptcha site can be added by providing the hCpatcha site-key with the associated secret for your account. You can add multiple sites: the site-key is the unique key that identifies the target site for a web protection request. The following command adds a site to your Approov account leaving all other settings at their default values:

$ approov web -hcaptcha -add <hCaptcha-Site-Key> -secret <hCaptcha-Secret> -domain <web-site-domain>
WARNING: changing the web configuration will have an immediate impact in production
ATTENTION: If you wish to continue then please type YES and return: YES
hcaptcha site <hCaptcha-Site-Key> added

Listing all the configured web protection properties shows that the new site has been added:

$ approov web -list
Site Key: <Account-Site-Key>
Token Lifetime: 120 seconds
hCaptcha:
  Optional: true
  Site Key: <hCaptcha-Site-Key>
    Domains:
      <web-site-domain>
    Min Score: 0.00
    Include IP: false
    Embed Result: false

The following table lists properties in order and provides their description:

Property Name Description
Site Key This is the Approov site key you need to use to identify your Approov account in web protection API requests
Token Lifetime This is the lifetime given to Approov tokens obtained from a web protection request. It may be changed using approov web -setTokenLifetime <seconds>.
hCaptcha The heading that separates hCaptcha specific properties from general properties and those of other integrations.
Optional Indicates whether an hCaptcha result must be provided for every web protection request or not. This defaults to true which allows a valid Approov token to be provided for a web protection request using another configured integration even if an hCaptcha result is not provided. This property may be unset using approov web -hcaptcha -setRequired and reset using approov web -hcaptcha -setOptional.
Site Key Displays the <hCaptcha-Site-Key> used to configure the site and serves as the heading for properties related to just this site. Note that the <hCaptcha-Secret> is not listed; the secret is the private key for the site and it is never displayed or logged by Approov. If you use the hCaptcha dashboard to change the site-key or secret, then you can always update the Approov site by adding it again with the new site-key.
Domains This lists the domains registered against the site; the entry is omitted if no domains were specified. Multiple domains can be registered by using the -domain flag multiple times. The backend hCaptcha lookup performed by Approov returns the domain that solved the hCaptcha challenge. If one or more domains are defined, then the returned domain must be found in this list. If no domains are registered, then a domain check is not performed. This facility can be used to selectively enable some of the domains that you have registered with the hCaptcha site definition. (We note that, when hCaptcha returns the domain, it strips the subdomain labels from the root. Please take care to ensure that the domains you register with Approov match the domains returned by hCaptcha results.)
Min Score The Score returned from an hCaptcha server-side lookup represents the probability that the user that solved an hCaptcha challenge is a human. Approov will return invalid tokens if the hCaptcha lookup returns a score lower than the configured minimum. The default, 0, will accept all scores. To change this value, use the -minScore <score> parameter when you add a site.
Embed Result If this is true *and you have chosen to use encrypted tokens*, JWEs, for the target API then the full hCaptcha result object will be included in generated Approov tokens. It defaults to false, use -embedResult when you add a site to turn it on.
Rate Limit This is not in the above list because no rate limit was specified, but it may be configured when you add a site to specify the maximum number of requests per minute: -rateLimit <limit>. Attempts to make requests beyond the limit will result in an error. The limit is specified in terms of the maximum number of requests per minute. If this is not specified, or set to 0, then no limit is imposed.

To change a site’s properties you simply add it again with all the properties required for the change. Each addition of the same site-key completely overwrites the previously stored entry.

Call Approov Web Protection with hCaptcha

Once the site is setup in Approov (and you’ve waited a couple minutes or so for the configuration to propagate), you can try retrieving an Approov token using a web protection request end-point. A simple async function to perform the request is provided below:

  async function fetchApproovToken(hcapToken) {
    const params = new URLSearchParams()
    params.append('approov-site-key', '<Approov-Site-Key>')
    params.append('api', '<Target-Approov-Protected-API-Domain>')
    params.append('hcaptcha-site-key', '<hCaptcha-Site-Key>')
    params.append('hcaptcha-token', hcapToken)

    const response = await fetch('https://web-1.approovr.io/attest', {
      method: 'POST',
      body: params
    })
    if (!response.ok) {
      throw new Error(response) // reject with a throw on failure
    }
    return response.text() // return the token on success
  }

Note that hcaptcha-ekey and hcaptcha-host can also be specified when requesting an Approov token. These parameters are described further in the section on endpoints.

The Approov token should never be logged in production code.

The precise code required to obtain the hCaptcha token and then call the above function will depend on the your preferred approach for using hCaptcha. The key flow for using Approov is to first obtain an hCaptcha token, then pass it to fetchApproovToken before passing that to your API backend. The simplest example provided for hCaptcha, hCaptcha’s Developer Guide, is to introduce a div with h-captcha class inside the form to be protected:

<form>
  ...
  <div class="h-captcha" data-sitekey="<hCaptcha-Site-Key>"></div>
  ...
</form>

In this approach, the example HTML doesn’t explicitly retrieve the hCaptcha token that’s obtained, it is just embedded in the form data and submitted from there. To use Approov in the same style, then you need to ensure that the Approov token is also added to a hidden form input field before the form is valid for submission. One approach for this, dervied from the above example, is provided below:

  <script>
    async function fetchApproovToken(hcapToken) {
      // as above...
    }
    function dataCallback(hcapToken) {
      fetchApproovToken(hcapToken)
        .then(approovToken => {
          const tokenEl = document.getElementById('approovToken')
          tokenEl.value = approovToken
          enableSubmitCheck()
        })
    }
    function enableSubmitCheck() {
      // a function triggered whenever we need to check if the submit button
      // should be enabled. Note that this is important, so that we can check
      // for the presence of the Approov token before the form is submitted.
      let shouldDisable = false
      // Other checks here ...
      const tokenEl = document.getElementById('approovToken')
      if (tokenEl.value == "") {
        shouldDisable = true
      }
      const submitEl = document.getElementById('submit')
      if (shouldDisable) {
        submitEl.setAttribute('disabled', true)
      } else {
        submitEl.removeAttribute('disabled')
      }
    }
  </script>
  <form>
    ...
    <div class="h-captcha" data-sitekey="<hCaptcha-Site-Key>" data-callback="dataCallback"></div>
    ...
    <input id="approovToken" type="hidden" value="" name="approovToken"></input>
    <input id="submit" type="submit" value="Submit" disabled>
  </form>

The downside of this approach is that a user action (clicking the Submit button) is required between the event that retrieves the Approov token and the form submission. This means that the default Approov token lifetime is likely to be too short. Extending the token lifetime is not recommended unless network latency is causing token exipiry before it can be processed by your backend API. The preferred solution is to override the form’s default submit behavior so that the Approov token fetch can be inserted in that flow. This second approach requires that your javascript code construct and issue the request to your API, however, this is also best practice because you can then adjust the request to precisely match the one used by your mobile app (which is highly likely to include some properties in headers, like the Approov token itself).

A modified version of the above, may look something like the example below:

  <script>
    async function fetchApproovToken(hcapToken) {
      // as above...
    }
    function submitForm() {
      const hcapToken = hcaptcha.getResponse()
      if (hcapToken != "") {
        fetchApproovToken(hcapToken)
          .then(approovToken => {
            // construct fetch request for API call ...
          })
          .catch(err => {
            // handle token fetch error
          })
      }
    }
  </script>
  <form action="javascript:submitForm()">
    ...
    <div class="h-captcha" data-sitekey="<hCaptcha-Site-Key>"></div>
    ...
    <input id="submit" type="submit" value="Submit">
  </form>

Other examples provided in the hCaptcha documentation can also be adapted in a similar vein to the one described.

Approov embed token claim for hCaptcha

You may wish to interrogate the hCaptcha results in your backend. hCaptcha properties are added to the embed claim of the Approov token which is a JSON object with a property called hcap:<SiteKey>, where the SiteKey is the hCaptcha site-key registered to your Approov account and used in the Approov token request. By default only the challenge timestamp and domain attributes are included in the token (using the same JSON structure as the full hCaptcha result), thus confirming that an hCaptcha lookup occurred. The following example shows the full decoded body of an Approov token with the default properties embedded from a hCaptcha request lookup:

{
  "exp": 1620398492,
  "ip": "1.2.3.4",
  "arc": "2VU2BFPIIO",
  "embed": {
    "hcap:59a3279b-4fb7-40ed-a1a5-4c7abe0a100c": {
      "challenge_ts": "2021-05-07T14:39:15",
      "hostname": "<domain-of-the-calling-webpage>"
    }
  }
}

If the -embedResult flag is used when you register the hCaptcha site and you have chosen to use encrypted tokens, JWEs, for the target API then the full results are included in the token. Note that the results from the hCaptcha lookup vary depending on your subscription level. A slightly abbreviated example is shown below:

{
  "exp": 1620398895,
  "ip": "1.2.3.4",
  "arc": "BIFTVTMS6I",
  "embed": {
    "hcap:59a3279b-4fb7-40ed-a1a5-4c7abe0a100c": {
      "success": true,
      "challenge_ts": "2021-05-07T14:45:58",
      "hostname": "<domain-of-the-calling-webpage>"
    }
  }
}

You should now be all setup to generate and receive web protection Approov tokens using hCaptcha. For the full reference on the web protection API endpoints and the more advanced features that are provided, please see the following reference section.

Web Protection Metrics Presentation

Once your web app is receiving tokens from Approov you can start looking at live and accumulated activity using Approov metrics. A full description of the available features are covered in the associated documentation: Metrics Graphs. That section of the docs lists all the metrics that may be used and describes their meaning. Launch the metrics dashboard with the following CLI command: approov metrics.

In short, the metrics for web protection based activity are integrated with the dashboards and graphs that also present mobile attestation activity. Live web protection metrics are included in the Live Metrics dashboard and they are prefixed with pass-web or fail-web. Web protection metrics are also presented by the Billing Usage, Hourly Metrics, Daily Metrics, and Monthly Metrics dashboards with associated extra prefixes.

Web Protection Service Reference

This section outlines the full specification of the Approov web protections interface. It presents the domains through which the service can be accessed; the end-point specifications; a description of how to use multiple web protection services in a single request; how to perform token binding; and how to troubleshoot problems.

Domains

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

  • web-1.approovr.io
  • web-1.approovr.com

Both of these domains point to the same service instances some of which are located in the EU and others in the US.

Endpoints

Two endpoints are provided, /attest and /attest-multi. On success, a 200 status is returned. The first returns an Approov token string for a single API. The second returns a JSON object that can contain tokens for multiple APIs; for the case when multiple calls to different APIs are required at the same time. Both endpoints accept the same parameters; /attest-multi accepts the api parameter multiple times. The previous examples demonstrate the handling of the single token string returned from /attest. On success, the /attest-multi endpoint returns a JSON object, with the same number of entries as there were api parameters and of the form:

{
  "<API-Domain-1>": "<Approov-Token-1>",
  "<API-Domain-2>": "<Approov-Token-2>",
  ...
}

A successful request, may return valid or invalid tokens indicating whether the checks on the associated web protections passed or failed. An error (400 status) is also possible; please see the section on Troubleshooting for a description of the possible reasons.

Both endpoints accept GET and POST methods. For GET requests all parameters must be URL encoded. For POST requests, parameters may be URL encoded or passed in the request body (as in the examples given previously). Mixing URL encoded parameters and parameters passed in the body is not permitted. Note that, some browsers enforce a maximum length on the URL which may cause some URL encoded requests to fail. hCaptcha in particular warn about this.

Parameter NameValue Description
approov-site-key Specifies 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. Only a single instance of this parameter is permitted.
api Specifies the domain of the target API. A single api parameter must be specified for every request. The /attest-multi end-point permits more than a single entry. Each api parameter must identify a new 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 An optional parameter used for token binding on the web channel. Only a single instance of this parameter is permitted. See the section below on Web Protection Token Binding.
fingerprintjs-token Specifies the browser token for a FingerprintJS subscription you have registered with Approov. If this is specified, then there must also be corresponding fingerprintjs-visitor-id and fingerprintjs-request-id parameters added to the request
fingerprintjs-visitor-id Specifies the visitor ID returned by a FingerprintJS browser lookup
fingerprintjs-request-id Specifies the request ID returned by a FingerprintJS browser lookup
hcaptcha-site-key Specifies the Site-Key for an hCaptcha site you have registered with Approov. If this is specified, then there must also be a corresponding hcaptcha-token parameter added to the request. Other hCaptcha parameters may also be added as specified below.Note that we don't currently provide a mechanism for specifying all the available hCaptcha siteverify parameters.
hcaptcha-token Specifies the token returned by an hCaptcha browser lookup
hcaptcha-ekey Specifies the challenge key returned by the hcaptcha.getRespKey() JS API call. This is an optional hCaptcha parameter. If present, there must be corresponding hcaptcha-site-key and hcaptcha-token parameters. See hCaptcha documentation; the next section provides an example showing how the parameter is defined.
hcaptcha-host Specifies the root host domain that must match with the hCaptcha response. If present, there must be corresponding hcaptcha-site-key and hcaptcha-token parameters. See hCaptcha documentation; the next section provides an example showing how the parameter is defined.
recaptcha-site-key Specifies the Site-Key for a reCAPTCHA site you have registered with Approov. If this is specified, then there must also be a corresponding recaptcha-token parameter added to the request
recaptcha-token Specifies the token returned by a reCAPTCHA browser lookup

Using Multiple Integrated Services to Protect Your API

Although previous examples all focus on a single wrapped web protection service, the endpoints support the addition of parameters from multiple web protections in a single request. In this case, valid Approov tokens are only provided if the web protection checks pass for all parameters. An example fetchApproovToken function that uses all the supported web protection services is provided below as an example:

  async function fetchApproovToken(visitorId, requestId, hcapToken, recapToken) {
    const params = new URLSearchParams()
    params.append('approov-site-key', '<Approov-Site-Key>')
    params.append('api', '<Target-Approov-Protected-API-Domain>')
    params.append('fingerprintjs-token', '<FingerprintJS-Browser-Token>')
    params.append('fingerprintjs-visitor-id', visitorId)
    params.append('fingerprintjs-request-id', requestId)
    params.append('hcaptcha-site-key', '<hCaptcha-Site-Key>')
    params.append('hcaptcha-token', hcapToken)
    params.append('hcaptcha-ekey', hcaptcha.getRespKey())
    params.append('hcaptcha-host', '<Site-Root-Domain>')
    params.append('recaptcha-site-key', '<reCAPTCHA-Site-Key>')
    params.append('recaptcha-token', recapToken)

    const response = await fetch('https://web-1.approovr.io/attest', {
      method: 'POST',
      body: params
    })
    if (!response.ok) {
      throw new Error(response) // reject with a throw on failure
    }
    return response.text() // return the token on success
  }

Web Protection Token Binding

Approov Token Binding provides a mechanism for linking an Approov token with other properties that will be sent to you API backend, like a User Authentication token. See the section on Token Binding for the mobile channel for further background.

The web channel has the same restrictions as the mobile channel, the bound data needs to be provided as a Base64URL encoded SHA256 sum. The Approov backend converts this to a standard Base64 value as it is added to the generated Approov token. This ensures that no Personal Identifiable Information(PII) can accidentally make it into the Approov backend services. The web protection endpoints respond with an error, if the payload does not conform to this restriction. The following function can be used to generate a valid digest for an arbitrary string:

  async function generatePayloadData(message) {
    if (message !== "") {
      const msgUint8 = new TextEncoder().encode(message);                   // encode as (utf-8) Uint8Array
      const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);   // hash the message
      const hashArray = new Uint8Array(hashBuffer);                         // convert buffer to byte array
      const hashString = String.fromCharCode.apply(null, hashArray)         // convert byte array to String (without char interpretation)
      const b64 = btoa(hashString)                                          // convert to b64
      return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '') // convert to b64url
    }
    return ""
  }

Note that the above function uses the restricted crypto.subtle.digest function. Browsers only permit calls to this function when the site is served from localhost or when it is served over https.

Note also that a Google search for Javascript that generates a Base64URL digest will result in many example code snippets that don’t generate the correct result. If you wish to use an alternative, please check that its output matches that of the above function.

The following snippet shows how to extend a fetchApproovToken implementation to append a payload argument whenever a non-empty message is provided:

  async function generatePayloadData(message) {
    // as above...
  }

  async function fetchApproovToken(..., message) {
    const params = new URLSearchParams()
    params.append('approov-site-key', '<Approov-Site-Key>')
    params.append('api', '<Target-Approov-Protected-API-Domain>')
    params.append(...)

    const payload = generatePayloadData(message)
    if (payload !== "") {
      params.append('payload', payload)
    }

    const response = await fetch('https://web-1.approovr.io/attest', {
      method: 'POST',
      body: params
    })
    if (!response.ok) {
      throw new Error(response) // reject with a throw on failure
    }
    return response.text() // return the token on success
  }

Troubleshooting Web Protection Errors

The Approov web protection endpoints normally respond with valid or invalid Approov tokens. However, if there is a problem with a request that prevents the normal web protection handler from completing then an error response will be generated. Also, if the underlying calls to the integrated protection APIs fail with an error, then this error is propagated to the response instead of generating failed tokens.

In these cases a 400 status and an error JSON object is returned from the endpoint. The JSON object is a sequence of key-value pairs, with the key indicating the type of error and the value providing extra information where appropriate. For some error types the value is just the empty string:

{
  "<error-code-1>": "<further-details>",
  "<error-code-2>": "",
  ...
}

An attempt is made to batch all problems with a request into the single error object to help make debugging quicker, however, this isn’t always possible. The following table lists the error keys with a description of their cause. Any extra data provided in the associated value may aid debugging.

Error CodeDescription
rqst-body-read-fail There was a problem reading the request body.
rqst-params-in-body-and-path The request contains parameters in the URL and in the message body; this is not permitted.
rqst-body-too-large The request message body is too large.
rqst-body-parse The request message body could not be decoded; it's probably not formatted correctly.
rqst-illegal-params The request included illegal paramters; the list of illegal entries is included in the value.
aprv-no-site-key Your Approov Site-Key was not included in the request parameters.
aprv-no-web-protect-params The request didn't contain any web protection parameters; no parameters for FingerprintJS, hCaptcha, or reCAPTCHA.
aprv-no-matching-account The specified Approov Site-Key didn't match any account.
aprv-no-web-protect-def The specified Approov Site-Key doesn't have any web protection integrations defined.
aprv-payload-param-invalid-size The request has a payload parameter, but it has an invalid size (32 bytes when decoded). It must be a Base64URL encoded SHA256 hash, see Web Protection Token Binding.
aprv-payload-not-b64url The request has a payload parameter, but it isn't Base64URL encoded. It must be a Base64URL encoded SHA256 hash, see Web Protection Token Binding.
aprv-api-no-targets The request parameters don't include any api target.
aprv-api-empty-target The request parameters include an api target whose value is the empty string.
aprv-api-duplicate-target The request parameters include more than one api entry with the same value.
aprv-api-too-many-targets The request parameters include more api targets than there are APIs defined for the target account.
aprv-api-not-found One or more api targets are not defined in your Approov account.
aprv-api-not-web-enabled One or more api targets have not been web enabled.
fpjs-config-not-found FingerprintJS parameters were used in the request but no FingerprintJS subscriptions are defined for the account.
fpjs-missing-required-params The request doesn't have FingerprintJS parameters, but a FingerprintJS check is required by the account, see the section on FingerprintJS configuration.
fpjs-param-mismatch Some of the parameters for a FingerprintJS check are specified but others are missing.
fpjs-empty-browser-tok A request with FingerprintJS parameters has the browser token parameter set to the empty string.
fpjs-empty-vis-id A request with FingerprintJS parameters has the visitor ID parameter set to the empty string.
fpjs-empty-rqst-id A request with FingerprintJS parameters has the request ID parameter set to the empty string.
fpjs-browser-tok-not-found The FingerprintJS Browser Token specified in a request doesn't match any defined in the selected Approov account.
fpjs-tok-check There was an error performing the FingerprintJS server-side check; the error string is included in the value.
hcap-config-not-found hCaptcha parameters were used in the request but no hCaptcha Sites are defined for the account.
hcap-missing-required-params The request doesn't have hCaptcha parameters, but an hCaptcha check is required by the account settings, see the section on hCaptcha configuration.
hcap-param-mismatch Some of the parameters for an hCaptcha check are specified but others are missing.
hcap-empty-site-key A request has an hCaptcha site key parameter set to the empty string.
hcap-empty-token A request has an hCaptcha token parameter set to the empty string.
hcap-site-key-not-found The hCaptcha Site key specified in a request doesn't match any defined in the selected Approov account.
hcap-token-check There was an error performing the hCaptcha server-side check; the error string is included in the value.
recap-config-not-found reCAPTCHA parameters were used in the request but no reCAPTCHA Sites are defined for the account.
recap-missing-required-params The request doesn't have reCAPTCHA parameters, but a reCAPTCHA check is required by the account settings, see the section on reCAPTCHA configuration.
recap-param-mismatch Some of the parameters for a reCAPTCHA check are specified but others are missing.
recap-empty-site-key A request has a reCAPTCHA site key parameter set to the empty string.
recap-empty-token A request has a reCAPTCHA token parameter set to the empty string.
recap-site-key-not-found The reCAPTCHA Site key specified in a request doesn't match any defined in the selected Approov account.
recap-token-check There was an error performing the reCAPTCHA server-side check; the error string is included in the value.