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 backend API 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 sign up 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 token fetch 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 Approov web protection. The following command adds a site to your Approov account leaving all other settings at their default values:
approov web -recaptcha -add your-reCAPTCHA-site-key -secret your-reCAPTCHA-secret
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
recaptcha site your-reCAPTCHA-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
reCAPTCHA:
Optional: true
Site Key: your-reCAPTCHA-site-key
Min Score: 0.00
Include IP: false
Embed Result: true
Max Result Lifetime: 0s
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 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 reCAPTCHA's server (see Max Result Lifetime below). When the Approov session ends, any result obtained from reCAPTCHA's server must be renewed. The refresh token lifetime may be changed using approov web -setRefreshTokenLifetime <seconds>. |
| reCAPTCHA | The heading that separates reCAPTCHA specific properties from general properties and those of other integrations. |
| Optional | Indicates whether it is optional to provide a reCAPTCHA 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 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. |
| Max Result Lifetime | This specifies the maximum lifetime of any result obtained from reCAPTCHA'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 reCAPTCHA
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 reCAPTCHA token obtained by calling the grecaptcha.getResponse() function. Once an Approov session has been initialized, the first Approov token fetch must include recent reCAPTCHA 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',
recaptchaSiteKey: 'your-reCAPTCHA-site-key'
})
// ...
// Initial token fetch of Approov session provides the reCAPTCHA token
const approovTokenPromise = Approov.fetchToken(api, {recaptchaToken: token})
// Subsequent token fetches during an Approov session don't require the reCAPTCHA
// token
const approovTokenPromise = Approov.fetchToken(api, {})
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 Approov.fetchToken before passing the resulting Approov token to your backend API. The discussion below focuses on changes to the Google docs for reCAPTCHA v3, however, the transformations are applicable to any of the reCAPTCHA flows.
The simplest example provided for reCAPTCHA v3 Checkbox style, Automatically bind the challenge to a button, is to introduce a button with the g-recaptcha class inside the form in which the reCAPTCHA 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 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 based on the reCAPTCHA v3 Documentation and shows how to integrate reCAPTCHA into a web page so that a hidden reCAPTCHA token is included with the form data.
<html>
<head>
<title>reCAPTCHA Demo</title>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<script>
function onSubmit(token) {
document.getElementById("demo-form").submit();
}
</script>
<form id="demo-form">
<!-- Form data inputs ... -->
<br/>
<button class="g-recaptcha" data-sitekey="your-reCAPTCHA-site-key"
data-callback='onSubmit'
data-action='submit'>Submit</button>
</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, 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 request headers, notably the Approov token.
The first step for integrating Approov into this flow is to modify the form's behavior to call a Javascript function (onClick) when the submit button is clicked. The onClick function programmatically invokes a reCAPTCHA challenge and includes the obtained reCAPTCHA token with the API request:
<html>
<head>
<title>reCAPTCHA Demo</title>
<script src="https://www.google.com/recaptcha/api.js?
render=your-reCAPTCHA-site-key">
</script>
</head>
<body>
<script>
function onClick(event) {
event.preventDefault();
// Convert the form data to JSON
// ...
// Perform the challenge and get a reCAPTCHA token
grecaptcha.ready(function() {
grecaptcha.execute('your-reCAPTCHA-site-key', {
action: 'submit'
}).then(function(reCaptchaToken) {
// Perform fetch call to API, passing reCAPTCHA token in a header
fetch("your-API-endpoint", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Recaptcha-Token': reCaptchaToken
},
body: JSON.stringify(/* form data converted to JSON */)
}).then(response => {
// Handle API response
})
})
})
}
</script>
<form id="demo-form">
<!-- Form data inputs ... -->
<br/>
<button id='submit'>Submit</button>
</form>
<script>
document.getElementById('submit').onclick = onClick
</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 call this in place of the reCAPTCHA 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>reCAPTCHA Demo</title>
<script src="https://www.google.com/recaptcha/api.js?
render=your-reCAPTCHA-site-key">
</script>
</head>
<body>
<script>
async function getRecaptchaV3Token() {
let recaptchaTokenPromise = new Promise(function(resolve, reject) {
try {
grecaptcha.ready(function() {
grecaptcha.execute('your-reCAPTCHA-site-key', {
action: 'submit'
}).then(function(recaptchaToken) {
resolve(recaptchaToken)
});
});
} catch (error) {
reject(error)
}
})
return recaptchaTokenPromise
}
// Fetches an Approov token and gets a fresh reCAPTCHA 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",
recaptchaSiteKey: 'your-reCAPTCHA-site-key',
})
// Get a fresh reCAPTCHA token
const recaptchaToken = await getRecaptchaV3Token()
// Fetch the Approov token
let approovToken = await Approov.fetchToken(api,
{recaptchaToken: recaptchaToken})
return approovToken
} else {
throw error
}
}
}
function onClick(event) {
event.preventDefault();
// Convert the form data to JSON
// ...
// Get an Approov token
fetchApproovToken("your-API-domain").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(/* form data converted to JSON */)
}).then(response => {
// Handle API response
})
})
}
</script>
<form id="demo-form">
<!-- Form data inputs ... -->
<br/>
<button id="submit">Submit</button>
</form>
<script>
document.getElementById('submit').onclick = onClick
</script>
</body>
</html>
The fetchApproovToken function programmatically invokes a reCAPTCHA challenge if required and obtains an Approov token. The onClick function is modified to call fetchApproovToken and to include the obtained Approov token with the API request. 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 API server. Google reCAPTCHA properties are added to the embed claim of the Approov token which is a JSON object with a property called recap:your-reCAPTCHA-site-key, where 'your-reCAPTCHA-site-key' 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:your-reCAPTCHA-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 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:your-reCAPTCHA-site-key": {
"success": true,
"challenge_ts": "2021-05-10T13:26:30Z",
"hostname": "<domain-of-the-calling-webpage>"
}
}
}
You should now be all set up to generate and receive web protection Approov tokens using Google reCAPTCHA. For the full reference on the web protection API and the more advanced features that are provided, please see the reference section after the integration summaries. 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.