Approov is an API security solution used to verify that requests received by your API services originate from trusted versions of your apps. The core Approov product is targeted at mobile apps, however, we provide several integrations with 3rd party web protection solutions so that a single back-end Approov check can be used to authorize API access. This quickstart shows how to use the integration with hCaptcha to add Approov tokens to your API calls.
hCaptcha is a popular service for determining if a browser is being operated by a human. A browser first retrieves a token from the hCaptcha 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 also provides further configuration options which extend the capabilities and/or customize behavior. For an overview of all enterprise level features, you should check out botstop.com.
Note that, hCaptcha web protection does not solve the same issue as Approov mobile app attestation which provides a very strong indication that a request can be trusted. However, for APIs that are used by both the mobile and web channels, a single check to grant access, simplifies the overall access control implementation. Approov's integration with hCaptcha requires that the backend first check that an Approov token is present and that it is correctly signed. Subsequently, the token claims can be read to differentiate between requests coming from the mobile or web channels and to apply any associated restrictions. If required, the full response from the hCaptcha check can be embedded in the Approov token to be used by that logic. We still recommend that you restrict critical API endpoints to only work from the Mobile App.
This quickstart provides a step-by-step guide to integrating hCaptcha with Approov in a web app using a simple demo API backend for obtaining a random shape. The integration uses plain Javascript without using any libraries or SDKs except those providing the hCaptcha integration. As such, you should be able to use it directly or easily port it to your preferred web framework or library.
If you are looking for another Approov integration you can check our list of quickstarts, and if you don't find what you are looking for, then please let us know here.
- What you will need?
- What you will learn?
- How it Works?
- Starting the Web Server
- Running the Shapes Web App without Approov
- Modify the Web App to use Approov with hCaptcha
- Running the Shapes Web App with Approov and hCaptcha
- What if I don't get Shapes
- Changing your own Web App to use Approov
- Content Security Policy
- Next Steps
- Access to a trial or paid Approov account
- Be registered with hCaptcha
- The
approovcommand line tool installed with access to your account - A web server or Docker installed
- The contents of the folder containing this README
- How to integrate hCaptcha with Approov on a real web app in a step by step fashion
- How to setup your web app to get valid Approov hCaptcha tokens
- A solid understanding of how to integrate hCaptcha with Approov into your own web app
- Some pointers to other Approov features
This is a brief overview of how the Approov cloud service and hCaptcha fit together. For a complete overview of how the frontend and backend work with the Approov cloud service, we recommend the Approov overview page on our website.
hCaptcha uses sophisticated machine learning models to tell humans and bots apart. This approach enables them to reduce the CAPTCHA friction for users and therefore improve the web experience.
In the combined Approov/hCaptcha flow, each API request made by the web app is handled such that:
- An hCaptcha token is fetched using the hCaptcha JS SDK
- A web protection request is made to the Approov cloud service passing the hCaptcha token for verification
- The Approov token returned from the Approov web protection request is added to the API request
- The API request is made as usual by the web app
The API backend, is always responsible for making the decision to accept or deny requests. This decision is never made by the client. In all flows using Approov, access is only granted if a valid Approov token is included with the request; the client (web app or mobile app) is unable to determine the validity of the token. Subsequent checks may further interrogate the contents of the Approov token and also check other credentials, such as user authorization, to refine how a request should be handled.
The Approov cloud service checks with the hCaptcha service that the received hCaptcha token is valid, and then the web protection request is handled such that:
- If the hCaptcha token check passes then a valid Approov token is returned to the web app
- If the hCaptcha token check fails then a legitimate looking Approov token will be returned
In either case, the web app, unaware of the token's validity, adds it to every request it makes to the Approov protected API(s).
This quickstart uses a static web app and so you can use any web server to run it. The web app root is: ./shapes-app/index.html.
If you have no other preference then please choose from one of the following options.
From the root of the quickstart run:
sudo docker-compose up localThis will first build the docker image and then start a container with it.
If your system has Python 3 installed then the simplest way to run the web server is to:
cd shapes-app python3 -m http.server If you have NodeJS installed you can do:
cd shapes-appnpm install http-server -g http-server --port 8000 Now visit http://localhost:8000 and you should be able to see:
Now that you have completed the deployment of your web app with one of your preferred web servers it is time to see how it works.
In the home page you can see three buttons, and you should now click in the UNPROTECTED button to see the unprotected Shapes web app":
Click on the HELLO button and you should see this:
This checks the connectivity by connecting to the endpoint https://shapes.approov.io/v1/hello.
Now press the SHAPE button and you will see this:
This contacts https://shapes.approov.io/v1/shapes to get a random shape.
Although the Shapes API is very simple, 2 end-points, with some code in a web app to control presentation, it is sufficient to demonstrate the required changes. The starting point in a real world scenario is the same. An API, probably using Approov to protect the mobile channel, and either a new requirement to enable access from the web or a desire to simplify the existing access that uses hCaptcha to protect against scripts and bots. The code changes below assume the former and take you through the steps to add both hCaptcha and Approov to the Shapes web app.
First, to simulate the web app working with an API endpoint protected with Approov tokens edit shapes-app/unprotected/assets/js/app.js and change the API_VERSION to v2, like this:
const API_VERSION = "v2"Now save the file and do a hard refresh in your browser with ctrl + F5, then hit the SHAPE button again and you should see this:
It gets the status code 400 (Bad Request) because this endpoint is protected by Approov.
The changes below will add the required Approov token to the API request by first issuing a hCaptcha check and then exchanging the returned hCaptcha token for an Approov token using the Approov hCaptcha integration.
We will need to modify two files, index.html and app.js.
If you suspect something has gone wrong while performing the changes you can always compare what you have with the files in the shapes-app/approov-hcaptcha-protected/ directory.
For example, the following commands will show the set of differences required for the two files that will change:
git diff --no-index shapes-app/unprotected/index.html shapes-app/approov-hcaptcha-protected/index.html git diff --no-index shapes-app/unprotected/assets/js/app.js shapes-app/approov-hcaptcha-protected/assets/js/app.js Overall, the changes required to add both these services are small.
Modify shapes-app/unprotected/index.html to load the hCaptcha JS SDK by adding the following markup after the </title> tag:
<script src="https://hcaptcha.com/1/api.js" async defer></script> Now we also need to bind the click events in the HELLO and SHAPE buttons to the hCaptcha script and have the fetchHello and fecthShape functions as the callbacks to receive the hCaptcha token.
Modify the file shapes-app/unprotected/index.html to add the necessary HTML changes for the HELLO and SHAPE buttons:
<div class="mt-2 py-2 border-t border-gray-300"> <div class="flex flex-wrap justify-center"> <div class="py-4 px-8 mt-0"> <button id="hello-button" class="h-captcha button-transition bg-blue-500 active:bg-blue-600 uppercase text-white font-bold hover:shadow-md shadow text-xs px-4 py-2 rounded outline-none focus:outline-none sm:mr-2 mb-1" type="button" data-sitekey="___HCAPTCHA_SITE_KEY___" data-callback='fetchHello' > Hello </button> </div> <div class="py-4 px-8 mt-0"> <button id="shape-button" class="h-captcha button-transition bg-blue-500 active:bg-blue-600 uppercase text-white font-bold hover:shadow-md shadow text-xs px-4 py-2 rounded outline-none focus:outline-none sm:mr-2 mb-1" type="button" data-sitekey="___HCAPTCHA_SITE_KEY___" data-callback='fetchShape' > Shape </button> </div> </div> </div>The additions can be summarised as follows:
- The
hCaptchacss class is required so that the hCaptcha SDK can identify the target element - The
data-sitekeyattribute is used to identify the site subscription to the hCaptcha servers - The
data-callbackattribute registers the callback function to receive the hCaptcha token
Now that we have made the hCaptcha script trigger off the click events in the HELLO and SHAPE buttons we need to remove the listeners for the click event in app.js. We also used fetchHello and fetchShape as the callbacks to receive the hCaptcha token, so we will also need to change them to handle the new parameter. The next section will show how to do it.
Modify the file shapes-app/unprotected/assets/js/app.js to remove the click handler registrations for the HELLO and SHAPE buttons from the load event handler which just leaves the navbar click handler in that function:
window.addEventListener('load', (event) => { const navbar = document.getElementById('toggle-navbar') navbar.addEventListener('click', (event) => toggleNavbar('example-collapse-navbar')) }) const API_VERSION = "v2" const API_DOMAIN = "shapes.approov.io" const API_BASE_URL = "https://" + API_DOMAINFollow this by adding required constants and the fetchApproovToken function, directly after the API_BASE_URL constant definition:
const APPROOV_WEB_PROTECT_URL = 'https://web-1.approovr.io/attest' const APPROOV_SITE_KEY = '___APPROOV_SITE_KEY___' const HCAPTCHA_SITE_KEY = '___HCAPTCHA_SITE_KEY___' // The hcaptcha token needs to be retrieved each time we want to make an // API request with an Approov Token. function fetchApproovToken(hCaptchaToken) { const params = new URLSearchParams() // Add it like `api.example.com` not as `https://api.example.com`. params.append('api', API_DOMAIN) params.append('approov-site-key', APPROOV_SITE_KEY) params.append('hcaptcha-site-key', HCAPTCHA_SITE_KEY) params.append('hcaptcha-token', hCaptchaToken) return fetch(APPROOV_WEB_PROTECT_URL, { method: 'POST', body: params }) .then(response => { if (!response.ok) { console.debug('Approov token fetch failed: ', response) throw new Error('Failed to fetch an Approov Token') // reject with a throw on failure } return response.text() // return the token on success }) }To fetch an Approov token we need an hCaptcha token which is passed as an argument to the fetchHello and fetchShape callbacks.
For example, when the user clicks the SHAPE button, the event will be handled by the hCaptcha script. If an hCaptcha token is obtained then it will be passed to the callback function, fetchShape. We need to ensure that the hCaptcha token makes it into the fetchApproovToken call and we do that by passing it through the intervening functions: makeApiRequest and addRequestHeaders.
Edit the file shapes-app/unprotected/assets/js/app.js to include the modified code for the following functions:
function addRequestHeaders(hCaptchaToken) { return fetchApproovToken(hCaptchaToken) .then(approovToken => { return new Headers({ 'Accept': 'application/json', 'Approov-Token': approovToken }) }) } function makeApiRequest(path, hCaptchaToken) { hideFromScreen() return addRequestHeaders(hCaptchaToken) .then(headers => fetch(API_BASE_URL + '/' + path, { headers: headers })) .then(response => handleApiResponse(response)) } function fetchHello(hCaptchaToken) { makeApiRequest(API_VERSION + '/hello', hCaptchaToken) .then(data => { document.getElementById('start-app').classList.add("hidden") document.getElementById('hello').classList.remove("hidden") }) .catch(error => handleApiError('Fetch from ' + API_VERSION + '/hello failed', error)) } function fetchShape(hCaptchaToken) { makeApiRequest(API_VERSION + '/shapes', hCaptchaToken) .then(data => { if (data.status >= 400 ) { document.getElementById('confused').classList.remove("hidden") return } let node = document.getElementById('shape') node.classList.add('shape-' + data.shape.toLowerCase()) node.classList.remove("hidden") }) .catch(error => handleApiError('Fetch from ' + API_VERSION + '/shapes failed', error)) }Note that, in the case you are migrating from an hCaptcha flow to an Approov flow, the changes are very minor. We would expect the same small changes to be required in your website at each point you construct requests that include an hCaptcha token. Depending on how your API calls are constructed, you may be able to make changes so that fetchApproovToken is called from a single point.
Before you can run the code it's necessary to obtain values for the placeholders, as described in the next section.
To use Approov with hCaptcha in the web app we need a small amount of configuration.
First, we need to use the Approov CLI to register the API domain that will be protected and have it specifically enabled for web protection. Note that all web-protection domains are also able to serve tokens for the mobile channel. Run the following CLI command to add or update the configuration for the shapes API:
approov api -add shapes.approov.io -allowWeb To configure Approov with an hCaptcha site you must first register with the hCaptcha service.
From the hCaptcha Dashboard you can add a site and then copy the site key and the API key necessary to use the hCaptcha service.
If your site key and API key were aaaaa12345 and bbbbb12345 respectively then the command to register it with Approov would look like this:
approov web -hCaptcha -add aaaaa12345 -secret bbbbb12345 When the hCaptcha token is passed to an Approov web-protection server for verification it, in turn, calls out to the hCaptcha servers before performing its checks on the result. The default check simply ensures that the result passes and issues a valid Approov token in that case. Further command line options can be used to control how Approov handles hCaptcha web protection, see the docs for details.
The code we added for the integration of hCaptcha with Approov has some placeholders for which we now have values.
Using the hCaptcha site key retrieved from the hCaptcha admin console we can now replace the HCAPTCHA_SITE_KEY directly in the code, or from the command line.
On Linux and MACs you can use the sed command:
sed -i "s|___HCAPTCHA_SITE_KEY___|aaaaa12345|" ./shapes-app/unprotected/index.html sed -i "s|___HCAPTCHA_SITE_KEY___|aaaaa12345|" ./shapes-app/unprotected/assets/js/app.js On Windows you can do it with:
get-content shapes-app\unprotected\index.html | %{$_ -replace "___HCAPTCHA_SITE_KEY___","aaaaa12345"} get-content shapes-app/unprotected/assets/js/app.js | %{$_ -replace "___HCAPTCHA_SITE_KEY___","aaaaa12345"} NOTE: Replace the dummy hCaptcha site key
aaaaa12345with your own one.
The Approov site key can be obtained with the following command:
approov web -list The Approov site key is the first Site Key in the output:
Site Key: 123a4567-abcd-12e3-9z8a-9b1234d54321 Token Lifetime: 10 seconds hCaptcha: Optional: true Site Key: aaaaa12345 Min Score: 0.00 Include IP: false Embed Result: false Now, replace the placeholder ___APPROOV_SITE_KEY___ directly in the code or from the command line.
On Linux and MACs you can use the sed command:
sed -i "s|___APPROOV_SITE_KEY___|123a4567-abcd-12e3-9z8a-9b1234d54321|" ./shapes-app/unprotected/assets/js/app.js On Windows you can do it with:
get-content shapes-app\unprotected\index.html | %{$_ -replace "___APPROOV_SITE_KEY___","123a4567-abcd-12e3-9z8a-9b1234d54321"} NOTE: Replace the dummy Approov site key
123a4567-abcd-12e3-9z8a-9b1234d54321with your own one.
Now that we have completed the integration of hCaptcha with Approov into the unprotected Shapes web app, it's time to test it again.
Refresh the browser with ctrl + F5 and then click on the SHAPES button and this time, instead of a bad request, we should get a shape:
This can be due to a lot of different causes, but usually is due to a typo, missing one of the steps or executing one of the steps incorrectly, but we will take you through the most probable causes.
Open the browser developer tools and check if you can see any errors in the console.
If you find errors related with the app.js file then fix them and try again, but always remember to refresh the browser with ctrl + F5 after updating a Javascript file.
If during localhost development you see CORS errors for any hcatpcha domain then follow their instructions for local development.
In shapes-app/unprotected/index.html, check that you are correctly:
- loading the hCaptcha SDK after the closing
</title>tag. - adding the required attributes to the
<button ...>HTML tag
Check that you are using the correct hCaptcha site key in index.html and app.js files:
- the placeholder
___HCAPTCHA_SITE_KEY___has been replaced - the site key doesn't have a typo
Check that you are using the correct Approov site key:
- the placeholder
___APPROOV_SITE_KEY___has been replaced - the Approov site key doesn't have a typo
Check that you have added the the shapes.approov.io API registered in your account. Use the following Approov CLI command and ensure the API is listed with web protection enabled.
approov api -list Check that you have correctly registered your hCaptcha site with the Approov CLI:
approov web -hcaptcha -list The output should look like this:
Optional: true Site Key: aaaaa12345 Min Score: 0.00 Include IP: false Embed Result: false If the hCaptcha site key is correct, then the next step is to check the hCaptcha API key. For security reasons the Approov CLI never outputs or returns the API key after it is set. To ensure the value is correct you can just re-register the site using the same CLI command and it will overwrite the entries.
Use the Approov CLI to see the Live Metrics and identify the cause of the failure.
approov metrics This will open your Approov Grafana metrics homepage. From there you can select the "Live Metrics" dashboard which includes web-protection request metrics updated every minute (max 2 mins for a request to be visible).
If the Approov web protection server is unable to complete a request then it will respond with an error.
See here the complete list of possible errors that can be returned by the Approov web protection server.
If the error is not displayed in the web page you may need to open the browser developer tools and inspect the json response payload for the request made to the Approov web protection server.
The Approov CLI can check Approov token validity and display the claims.
Open the browser developers tools and from the network tab grab the Approov token from the request header Approov-Token and then check it with:
approov token -check <approov-token-here> In the output of the above command look for the embed claim that contains the response details for the hCaptcha token.
Example of an Approov web protection token containing an embed claim with partial hCaptcha results:
{ "exp": 1622476181, "ip": "1.2.3.4", "embed": { "hcap:aaaaa12345": { "challenge_ts": "2021-06-04T17:36:07.000000Z", "hostname": "example.com" } } }(The output of the Approov CLI is not formatted as above.)
NOTE: You can get the full results from hCaptcha verification if your API uses encrypted tokens and you specify
-embedResultwhen you configure the hCaptcha site with the Approov CLI
This quick start guide has taken you through the steps of adding hCaptcha into Approov in the shapes demonstration web app.
You can follow the same approach to add Approov with hCaptcha into your own web app.
Remember to do an audit of your API to check which end-points should be enabled for web access. When necessary, extend the backend token check to differentiate between mobile app and web app tokens and use that to restrict the access to more sensitive end-points. Once the backend is ready, enable the Approov web protection by adding the -allowWeb flag whenever you register or re-register an API with the Approov CLI.
The Shapes example app uses the API endpoint https://shapes.approov.io/v2/shapes hosted on Approov's servers and you can see the code for it in this Github repo.
If you want to integrate Approov into your own web app you will need to integrate an Approov token check in the backend. Since the Approov token is simply a standard JWT this is usually straightforward.
Check the Backend integration examples that provide a detailed walk-through for specific programming languages and frameworks.
In the content-src policy of your current web app you will need to add the domains for Approov:
connect-src https://your-domains.com https://web-1.approovr.io/; hCaptcha works with an iframe therefore you need to allow it with:
frame-src https://newassets.hcaptcha.com/captcha/; Finally, add the static and dynamic hCaptcha scripts to your script-src policy:
script-src 'self' https://hcaptcha.com/1/api.js https://newassets.hcaptcha.com/captcha/; You can check the Content Security Policy for your site here or testing it in conjunction with all the other security headers here.
If you wish to explore the Approov solution in more depth, then why not try one of the following links as a jumping off point:





