reCAPTCHA - error-codes: 'missing-input-response', 'missing-input-secret' when verifying user's response (missing details on POST)
Asked Answered
C

3

83

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.

Curtate answered 20/9, 2018 at 0:54 Comment(0)
C
221

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:

  1. 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').
  2. 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.

Curtate answered 20/9, 2018 at 0:54 Comment(8)
Thank you! The POST parameters solution worked for me.Ectopia
Excellent. This worked for me as well. If you are using axios, this is very helpful: github.com/axios/…Lucretialucretius
Perfect. appending the secret and response to the verify URL worked. Thank youFrohman
Even assuming that Google makes sense of this behavior (?!), There is no information in Google's documentation, not even now.Subtype
The documentation shows what the API request accepts for it's POST parameters. However, only the content type is not specified which is application/x-www-form-urlencoded from what we can figure out. I have included a link to the official Google documentation for the verify request so future users can be informed of any changes. (Thanks!)Curtate
Thank you! I do find the documentation obscure. Your explanation is clear as day - should do a PR on the docco!Iceland
This is no sense, the API returns in JSON, but the parameters NEED be in form-urlencoded... Congrats to Google...Caerleon
@IsraelLinsAlbuquerque totally agree! The biggest software company in the world but even after Recaptcha v2 being online for 10 years (!) they didnt add this to the documentation, or mention it in any way... bizarreVillanovan
D
3

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
  }
});
Dorise answered 8/7, 2021 at 11:54 Comment(1)
Encoding it safely is required, unless we always assume that the parameter values are url-safe.Hardy
V
0

I've got same issue with Turnstile CloudFlare captcha, and somehow the internet led me to this very SO page.

But the above anwsers helped me a lot and in my case this was a missing value in header

'Content-Type': 'application/json'

Vanthe answered 28/3 at 7:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.