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 backend API 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 sign up 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 hCaptcha 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 Approov web protection. The following command adds a site to your Approov account leaving all other settings at their default values:
approov web -hcaptcha -add your-hCaptcha-site-key -secret your-hCaptcha-secret -domain your-web-site-domain
This will output something like the following and requires confirmation:
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 your-hCaptcha-site-key added
Listing all the configured web protection properties shows that the new site has been added:
approov web -list
This will output something like the following:
Site Key: your-Approov-site-key
Token Lifetime: 120 seconds
Refresh Token Lifetime: 7200 seconds
hCaptcha:
Optional: true
Site Key: your-hCaptcha-site-key
Domains:
your-web-site-domain
Min Score: 0.00
Include IP: false
Embed Result: false
Max Result Lifetime: 0s
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 the Approov web protection service. It may be changed using approov web -setTokenLifetime <seconds>. |
| Refresh Token Lifetime | This specified the lifetime of the refresh token and determines the duration of an Approov session. The length of an Approov session is also limited by the maximum lifetime of any result obtained from hCaptcha's server (see Max Result Lifetime below). When the Approov session ends, any result obtained from hCaptcha's server must be renewed. The refresh token lifetime may be changed using approov web -setRefreshTokenLifetime <seconds>. |
| hCaptcha | The heading that separates hCaptcha specific properties from general properties and those of other integrations. |
| Optional | Indicates whether it is optional to provide an hCaptcha result for the initial token fetch of an Approov session or not. This defaults to true which allows a valid Approov token to be provided for web protection 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. |
| Max Result Lifetime | This specifies the maximum lifetime of any result obtained from hCaptcha's server, after which it must be renewed. It can be set by using the -maxResultLifetime <value> parameter when you add the subscription. A value of 0, the default, denotes that it is equal to the refresh token lifetime. The duration of an Approov session is determined by the smaller of the maximum result lifetime and the refresh token lifetime (see Refresh Token Lifetime above). |
| 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 set up in Approov (and you've waited a couple minutes or so for the configuration to propagate), you can try retrieving an Approov token using the Approov web SDK. For the full documentation of the Approov web SDK please refer to the reference section. The web SDK can be downloaded using the Approov command line tool:
approov sdk -packageID approov.js.zip -getClientPackage approov.js.zip
This writes the latest available web SDK package to the approov.js.zip file. Unzip the file, copy approov.js into your project and load it as part of your web app.
Some example invocations of the Approov web SDK functions are provided below, where the api argument is your Approov protected API domain and token is the hCaptcha token obtained by calling the hcaptcha.getResponse() function. Once an Approov session has been initialized, the first Approov token fetch must include a recent hCaptcha token, but this is not required for any subsequent Approov token fetches during the same Approov session:
// 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',
hcaptchaSiteKey: 'your-hCaptcha-site-key'
})
// ...
// Initial token fetch of Approov session provides the hCaptcha token
const approovTokenPromise = Approov.fetchToken(api, {hcaptchaToken: token})
// Subsequent token fetches during an Approov session don't require the hCaptcha
// token
const approovTokenPromise = Approov.fetchToken(api, {})
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 Approov.fetchToken before passing passing the resulting Approov token to your backend API. 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="your-hCaptcha-site-key"></div>
...
</form>
In this approach, the example HTML doesn't explicitly retrieve the hCaptcha token that is obtained, it is just embedded in the form data and submitted from there. To use Approov in the same style, you would need to ensure that the Approov token is also added to a hidden form input field before the form is submitted. 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 Approov token lifetime is likely to be too short. Extending the token lifetime is not recommended unless network latency is causing token expiry before it can be processed by your backend API.
The example code below is taken from the hCaptcha Developer Guide and shows how to integrate hCaptcha into a web page so that when the captcha is solved successfully, a hidden hCaptcha token will be included with the form data.
<html>
<head>
<title>hCaptcha Demo</title>
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
</head>
<body>
<form action="" method="POST">
<input type="text" name="email" placeholder="Email" />
<input type="password" name="password" placeholder="Password" />
<div class="h-captcha" data-sitekey="your-hCaptcha-site-key"></div>
<br />
<input type="submit" value="Submit" />
</form>
</body>
</html>
This approach does not permit the form submission to be intercepted for the purpose of including an Approov token with the form data. The solution is to override the form's default submit behavior so that the Approov token fetch can be inserted in the flow. This approach requires that your javascript code constructs and issues the request to your API. This is 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, notably the Approov token itself.
The first step for integrating Approov into this flow is to make the hCaptcha widget invisible and to modify the form's default submit behavior to call a Javascript function (submitForm) when the submit button is clicked. The submitForm function programmatically invokes an hCaptcha challenge and includes the obtained hCaptcha token with the API request:
<html>
<head>
<title>hCaptcha Demo</title>
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
</head>
<body>
<script>
function submitForm(event) {
event.preventDefault();
// Convert the form data to JSON
const data = {
email: document.getElementById("email").value,
password: document.getElementById("password").value
}
// Perform the challenge and get a hCaptcha token
hcaptcha.execute({async: true}).then(hCaptchaToken => {
// Perform fetch call to API, passing hCaptcha token in a header
fetch("your-API-endpoint", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Hcaptcha-Token': hCaptchaToken
},
body: JSON.stringify(data)
}).then(response => {
// Handle API response
})
})
}
</script>
<form>
<input id="email" type="text" name="email" placeholder="Email" />
<input id="password" type="password" name="password" placeholder="Password" />
<div class="h-captcha" data-sitekey="your-hCaptcha-site-key"
data-size="invisible"></div>
<br />
<button id="submit">Submit</button>
</form>
<script>
document.getElementById('submit').onclick = submitForm
</script>
</body>
</html>
Once your web-app is converted to use the same API as your mobile app, there are few changes needed to add Approov. Add a function (fetchApproovToken) to obtain an Approov token and then use this in place of the hCaptcha challenge invocation and include the obtained Approov token in the API request. The function fetchApproovToken shows how to integrate Approov web protection in a single function using lazy (re-)initialization of Approov sessions:
<html>
<head>
<title>hCaptcha Demo</title>
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
</head>
<body>
<script>
// Fetches an Approov token and gets a fresh hCaptcha token if required
async function fetchApproovToken(api) {
let {Approov, ApproovSessionError} = await import("./approov.js")
try {
// Try to fetch an Approov token
let approovToken = await Approov.fetchToken(api, {})
return approovToken
} catch (error) {
if (error instanceof ApproovSessionError) {
// If Approov has not been initialized yet or the Approov session has
// expired, initialize and start a new session
await Approov.initializeSession({
approovHost: "web-1.approovr.io",
approovSiteKey: "your-Approov-site-key",
hcaptchaSiteKey: "your-hCaptcha-site-key",
})
// Perform the challenge and get a fresh hCaptcha token
const hcaptchaToken = await hcaptcha.execute({async: true})
// Fetch the Approov token
let approovToken = await Approov.fetchToken(api,
{hcaptchaToken: hcaptchaToken})
return approovToken
} else {
throw error
}
}
}
function submitForm(event) {
event.preventDefault();
// Convert the form data to JSON
const data = {
email: document.getElementById("email").value,
password: document.getElementById("password").value
}
// Get an Approov token
fetchApproovToken("shapes.approov.io").then(approovToken => {
// Perform fetch call to API, passing Approov token in a header
fetch("your-API-endpoint", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Approov-Token': approovToken
},
body: JSON.stringify(data)
}).then(response => {
// Handle API response
})
})
}
</script>
<form>
<input id="email" type="text" name="email" placeholder="Email" />
<input id="password" type="password" name="password" placeholder="Password" />
<div class="h-captcha" data-sitekey="your-hCaptcha-site-key"
data-size="invisible"></div>
<br />
<button id="submit">Submit</button>
</form>
<script>
document.getElementById('submit').onclick = submitForm
</script>
</body>
</html>
The fetchApproovToken function programmatically invokes an hCaptcha challenge if required and obtains an Approov token. The submitForm function is modified to call fetchApproovToken and to include the obtained Approov token with the API request. 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 system. hCaptcha properties are added to the embed claim of the Approov token which is a JSON object with a property called hcap:your-hCaptcha-site-key, where the your-hCaptcha-site-key part 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:your-hCaptcha-site-key": {
"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:your-hCaptcha-site-key": {
"success": true,
"challenge_ts": "2021-05-07T14:45:58",
"hostname": "<domain-of-the-calling-webpage>"
}
}
}
You should now be all set up to generate and receive web protection Approov tokens using hCaptcha. For the full reference on the web protection API and the more advanced features that are provided, please see the following reference section. For how to verify Approov tokens in your backend API server please see the section Backend Integration of the Approov Reference Guide and for information as to how to make use of the DPoP (Demonstrating Proof of Possession) token please refer to DPoP Token Use and Verification.