4
votes
[ { hostname: 'www.google.com',
    path: '/recaptcha/api/siteverify',
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Content-Length': 556 } },
  '{"secret":"XXXUseThisForCommunicationBetweenYourSiteAndGoogleXXX","response":"03AHJ_VuusXdr5IdGpNzQPRjedGs-Le066Fx9r-Lk1gIfLqlzwxapPx70_LukmcOsw3x-m2DSfpvQVylx060H9IjFP82fy7505_t_rjSivauiwBUyQPrBMp5kTRviq_DD1L2mVMTTrBieUMlQM69AIuG3KwmdOQMyMJS2iJdRuRNnvAmDlPSejkASR4X-7c4IIP3NoMb52Qsl9QPeU6kGaPtxqmf1IpNwbSC3bzLXQD-QV1aI4GgaeqSPfOO8EPfISJMQ5kbCd9wqAwHqDAXMtNSvz10Ty30R71HqmsSk7YHddFQhei1L6y9j7nxnY5QtAxHehhpYwJVNjI96hxeIaG58_CQHGbAufy4aPGAlf-zJ6be_Xtdzd4AnHxiX9OuCKQI8eQlh6DZLGaymxXDmPNu4TijGyyu0VeTPTTKf12zVUg86_0ZmszWZDtALjnNnxBH7bZqrgWXhy","remoteip":"00.00.000.000"}' ]

If I post that and google returns this:

{ success: false, 'error-codes': [ 'missing-input-response', 'missing-input-secret'] }

I don't see what is happening wrong

https://www.google.com/recaptcha/admin#site/XXXXXX?setup says:

When your users submit the form where you integrated reCAPTCHA, you'll get as part of the payload a string with the name "g-recaptcha-response". In order to check whether Google has verified that user, send a POST request with these parameters: URL: https://www.google.com/recaptcha/api/siteverify secret(required) - XXXXXX, response(required) - The value of 'g-recaptcha-response', remoteip - The end user's ip address.

I have clearly sent all these things! What could be happening here? The error does not say they are wrong, it says they are 'missing'

And the above Quoted text from google clearly says POST not GET Google reCAPTCHA: how to get user response and validate in the server side

But if I try a GET request then the response is [ null, 400, undefined ]

UPDATE

As @mscdex pointed out application/x-www-form-urlencoded is required but the responce still said that it was missing the secret so I url encoded this instead as I figured something bad may be happening to the item at the start of the object.

{'_':'_','secret':'XXXXXX','response':'whateverXXYsgTSG','remoteip':'00.00.000.000'}

And finally it worked:

[ { hostname: 'www.google.com',
    path: '/recaptcha/api/siteverify?',
    method: 'POST',
    headers: 
     { 'Content-Type': 'application/x-www-form-urlencoded',
       'Content-Length': 548 } },
  '"_=_&secret=XXXXXX&response=03AHJ_VuurQFgsftybLlvrdGOwXfNneWp4v7FPJJbOD9CGpiHAkFBaiNy7YWXcHrAkU6SPU5UZpgKCptU3gRX5OPqXEh2qqP3nXJpiBWoxFW_Iv05P2UA23rzzZk0ecScmMSL1PP1uyBCdJ08HpAWEuz2PzL6m6u71k09xQbVbPZ5KT6qnb-mdPNyEkdBxtc9a5oYpnOoHg7ax6q4Ms4Lis4qrNBLCavKmYZ6vAmYitSEI0a0GERnlI3wLSvayhc-Yygv1koKIjg2q8GHXV1UhKLzBa8t8x2ibRBNwXUMBFs3Qj_lfwgiTNtIaU3kEAFPULJulZDOsAcovKpjk5xkyMM2C5YDGYMioeyOMl9ZmyyvkwfrrRe8e9o_tD6SaTTSAcrcxsfYGm-w0_CDbsa2IWSkjiMN-2B9SClOZJGXXVXVIuIYClIK3XuUvTsObCzxJAq2IKwwMTtYX&remoteip=00.00.000.000"' ]
[ { success: true }, 200, undefined ]

But I would like to do this properly not hacky So if anyone can answer how to do it properly that would be swell!

var JSON={
        https:require('https')
    ,   toquery:require('../node_modules/querystring').stringify
    ,   stringify:require('../node_modules/json-stringify-safe')
    ,   parse:require('../node_modules/try-json-parse')
    ,   get:function(url,callback){process.env.NODE_TLS_REJECT_UNAUTHORIZED="0";var req=JSON.https.request(url,function(res){var buffer='';res.setEncoding('utf8');res.on('data',function(chunk){buffer+=chunk;});res.on('end',function(){try{var data=JSON.parse(buffer);callback(data,res.statusCode);}catch(e){console.log(e);}});});req.end();}
    ,   post:function(url,path,data,type,callback){if(!callback){callback=type;type=undefined;}data=JSON.stringify(data);var options={hostname:url,path:path,method:'POST',headers:{'Content-Type':type||'application/json','Content-Length':data.length}};console.dir([options,data]);var req=JSON.https.request(options,function(res){var buffer='';res.setEncoding('utf8');res.on('data',function(chunk){buffer+=chunk;});res.on('end',function(){try{var data=JSON.parse(buffer);callback(data,res.statusCode);}catch(e){console.log(e);}});});req.write(data);req.end();}
        };

JSON.post(
        'www.google.com'
    ,   '/recaptcha/api/siteverify?'
    ,   JSON.toquery({'_':'_','secret':'XXXX','response':response,'remoteip':remoteip})
    ,   'application/x-www-form-urlencoded'
    ,   function(data,result,statusCode){
            console.dir([data,result,statusCode]);
            if(result.success){}
            else{}
            });
2
Are you sure the API allows an application/json POST submission? Perhaps it's expecting an application/x-www-form-urlencoded POST instead?mscdex
Updated my question based on thisBen Muircroft
Ok, so show how are you making the request.mscdex
Updated to show the codeBen Muircroft
That looks alright to me and you said it works. Why do you call it "hacky?"mscdex

2 Answers

1
votes

Here is how I do it in one of my project using superagent. This is my recaptcha-helper.js

var request = require("superagent");

var config = {
  recaptcha: {
    secret: "XXXXX",
    url: "https://www.google.com/recaptcha/api/siteverify",
  },
};

var ERROR_CODES = {
  "missing-input-secret": "Unexpected Server Error (1)",
  "invalid-input-secret": "Unexpected Server Error (2)",
  "missing-input-response": "Missing reCAPTCHA value",
  "invalid-input-response": "Invalid reCATPCHA value",
};

exports.getErrorCode = function (errorCode) {
  if (Array.isArray(errorCode)) {
    var errors = errorCode.map(function (code) {
      return exports.getErrorCode(code);
    });
    return errors.join("\n");
  }
  return ERROR_CODES[errorCode] ||
    (errorCode ? ("Unexpected reCAPTCHA error: " + errorCode) : "Unexpected reCAPTCHA error");
};

exports.parseResponse = function (err, res) {
  if (err) {
    return { success: false, error: err };
  } else if (!res.body.success) {
    var error = new Error(exports.getErrorCode(res.body["error-codes"]));
    return { success: false, error: error };
  } else {
    return { success: true };
  }
};

exports.verify = function (response, ip) {
  if (process.env.NODE_ENV === "test") {
    return response ? Promise.resolve() :
      Promise.reject(new Error("Test reCAPTCHA Error"));
  }

  return new Promise (function (resolve, reject) {
    request.post(config.recaptcha.url)
    .type("form")
    .accept("json")
    .send({
      secret: config.recaptcha.secret,
      response: response,
      remoteip: ip,
    })
    .end(function (err, res) {
      var parsedRes = exports.parseResponse(err, res);
      return parsedRes.success ? resolve() : reject(parsedRes.error);
    });
  });
};

And you can use it doing

var captchaHelper = require('./recaptcha-helper');

captchaHelper.verify(req.body.captcha, req.ip)
  .then(function () {
    // on success
  }).catch(function (err {
   // on error
  });
0
votes

I solved this by passing secret and response as a query parameter:

Example :

axios.post("https://www.google.com/recaptcha/api/siteverify?secret="
+ 'XXXXXX' + "&response=" + response + "&remoteip=" + req.connection.remoteAddress);