javascriptnode.jscorsgoogle-oauthcors-anywhere

Can't call google oauth with Javascript/Heroku CORS anywhere or Node


I need to use Google's limited input device sign in with a code on screen. This isn't available to Web Applications so I have to use the Other type, therefore I cannot set CORS so I have set up a proxy on Heroku using this.

This works fine:

curl https://xxxxx.herokuapp.com/https://accounts.google.com/o/oauth2/device/code -H "x-requested-with: *" -d "client_id=xxxxx.apps.googleusercontent.com&scope=profile"

This returns and error: invalid_request

var xhr = new XMLHttpRequest();
xhr.onload = function() {
  console.log(this.responseText);
  var data = JSON.parse(this.responseText);
  console.log(data);
}
xhr.open("POST", 'https://xxxxxxx.herokuapp.com/https://accounts.google.com/o/oauth2/device/code', true);
xhr.setRequestHeader('x-requested-with', '*');
xhr.send(JSON.stringify({
  client_id: 'xxxxxxxxxxxxxxxx.apps.googleusercontent.com',
  scope: 'profile'
}));

And so does this:

var querystring = require('querystring');
var request = require('request');

request({
    uri: 'https://xxxx.herokuapp.com/https://accounts.google.com/o/oauth2/device/code',
    body: querystring.stringify({
      client_id: 'xxxxxx.apps.googleusercontent.com',
      scope: 'profile'
    }),
    headers: {
      'x-requested-with': '*'
    },
    method: 'POST'},
    function (error, response, body) {
        console.log(error)
        console.log(response)
        console.log(body)
    }
);

What am I doing wrong?


Solution

  • Change the code for your XHR request to this:

    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      console.log(this.responseText);
      var data = JSON.parse(this.responseText);
      console.log(data);
    }
    xhr.open("POST", 'https://xxxxxxx.herokuapp.com/https://accounts.google.com/o/oauth2/device/code', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send("client_id=xxxxx.apps.googleusercontent.com&scope=profile");
    

    That is, send a Content-Type: application/x-www-form-urlencoded request header, and a request body in the format client_id=xxxxx.apps.googleusercontent.com&scope=profile.

    At least that’s what you must do if you want to emulate the curl request shown in the question — because that curl invocation causes the literal value of the argument to the -d option to be sent as the request body — client_id=xxxxx.apps.googleusercontent.com&scope=profile — with a Content-Type: application/x-www-form-urlencoded request header.

    You can confirm that yourself by adding --trace-ascii /dev/stdout to the curl invocation and examining the trace that curl logs to the console.

    In contrast, the code for the XHR request in the question as-is sends a request body in the format {client_id: "xxxxxxxxxxxxxxxx.apps.googleusercontent.com", scope: "profile"} with a Content-Type: text/plain;charset=UTF-8 request header.

    See too the “Request device and user codes” section of the related Google documentation, which also shows a Content-Type: application/x-www-form-urlencoded request header is needed.