reCAPTCHA - error-codes: 'missing-input-response', 'missing-input-secret' when verifying user's response (missing details on POST)
I am setting an invisible reCAPTCHA in my web application and having trouble verifying the user's response. (even though I am passing the correct POST parameters)
I am programmatically invoking the challenge by calling grecaptcha.execute();
on the client-side. And submitting the form afterwards (registrationForm.submit();
) using the recaptcha callback:
<div class="g-recaptcha"
data-sitekey="SITE_KEY"
data-callback="onSubmit"
data-size="invisible">
</div>
Now after reading "Verifying the user's response" documentation, I figured that the response token is passed as a POST parameter to g-recaptcha-response
:
For web users, you can get the user’s response token in one of three ways:
- g-recaptcha-response POST parameter when the user submits the form on your site
- ...
So I am using Fetch to create a POST request on the server side to the verification endpoint with the required body data:
verify(req, res, next) {
const VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify";
return fetch(VERIFY_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
secret: process.env.RECAP_INVIS_SECRET_KEY,
response: req.body['g-recaptcha-response'],
}),
})
.then(response => response.json())
.then(data => {
res.locals.recaptcha = data;
return next();
});
}
But I keep getting the following response:
{ success: false, error-codes: [ 'missing-input-response', 'missing-input-secret' ] }
Even though I am passing the response and secret as JSON data in the POST body.
Am I doing something wrong? Regards.
Doing a bit of research and digging around the reCaptcha Google forums, It seems that this endpoint only accepts the default content type; application/x-www-form-urlencoded
.
Which means you should not use JSON to send your response token and site key. Instead, send the value as how the application/x-www-form-urlencoded
defined:
Forms submitted with this content type must be encoded as follows:
- Control names and values are escaped. Space characters are replaced by '+', and then reserved characters are escaped as described in [RFC1738], section 2.2: Non-alphanumeric characters are replaced by '%HH', a percent sign and two hexadecimal digits representing the ASCII code of the character. Line breaks are represented as "CR LF" pairs (i.e., '%0D%0A').
- The control names/values are listed in the order they appear in the document. The name is separated from the value by '=' and name/value pairs are separated from each other by '&'.
Therefore, you got two ways of doing this, either by passing the POST parameters through the URL (query strings) and sending it as a POST request:
https://www.google.com/recaptcha/api/siteverify?secret=${SECRET_KEY}&response=${req.body['g-recaptcha-response']}
or appending the data to the body manually like so:
verify(req, res, next) {
const VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify";
return fetch(VERIFY_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `secret=${SECRET_KEY}&response=${req.body['g-recaptcha-response']}`,
})
.then(response => response.json())
.then(data => {
res.locals.recaptcha = data;
return next();
});
}
The official Google documentation can be found here:
Recaptcha - Verifying the user's response.
Extending on U-ways answer above (thanks for that), if you wanted to keep a JS object structure instead of formatting inline params, you could make use of URLSearchParams
and Object.entries
:
const recaptchaBody = {
secret: RECAPTCHA_SECRET,
response: recaptchaResponse,
};
// Check recaptcha validity
fetch("https://www.google.com/recaptcha/api/siteverify", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams(Object.entries(recaptchaBody)).toString(),
})
.then((res) => res.json())
.then((data) => {
// If it's not a success..
if (!data?.success) {
// ERROR
}
});