0

Here is my http POST code, running on localhost:

if(headers['Content-Type'] == undefined)
        headers['Content-Type'] = 'application/x-www-form-urlencoded';

    var post_options = {
          host: host,
          path: path,
          port: port,
          method: 'POST',
          headers: headers
    };

    if(headers['Content-Type'] == "application/json"){
        post_options["json"] = true;
        var post_data = JSON.stringify(body);
    }else
        var post_data = querystring.stringify(body);

     var post_req = http.request(post_options, function(res) {

          var body = '';

          console.log("INSIDE CALLBACK HTTP POST");

          res.setEncoding('utf8');

          res.on('data', function (chunk) {
              body += chunk;
              console.log('Response: ' + chunk);
          });

          res.on('end', function () {
            var post = querystring.parse(body);
            console.log("FINAL BODY:",post);
          });

          //console.log("RESPONSE in http POST:",res);

      });

      // post the data
      console.log("WRITING HTTP POST DATA");
      var sent_handler = post_req.write(post_data);

      console.log("POST_REQ:",post_req);
      console.log("sent_handler:",sent_handler);

      post_req.end();

Here is the information I send to instagram exactly:

  • host = "api.instagram.com"
  • path = "/oauth/access_token"
  • body as follows:

    body["client_id"] = CLIENT_ID;

    body["client_secret"] = CLIENT_SECRET;

    body["grant_type"] = "authorization_code";

    body["redirect_uri"] = AUTHORIZATION_REDIRECT_URI;

    body["code"] = login_code;

    body["scope"] = "public_content";

  • headers = {} (empty, assumes headers['Content-Type'] == undefined as true)

  • Important: sent_handler returns false

  • The console.log for "FINAL BODY" (variable post) returns "{}"

To note: communications using curl with the api instagram works. So I really believe the problem is in some part of this code in nodejs.

Does anyone have any idea? Please ask if there is need for more information

Fane
  • 1,978
  • 8
  • 30
  • 58
  • Please don't mix and match omitting braces from your if statements It's just asking for a disaster, and it dramatically reduces readability. – brandonscript Jun 21 '17 at 23:51
  • @brandonscript any idea on what might be happening with the requests though? – Fane Jun 22 '17 at 13:37

1 Answers1

0

Ok so there are three major problems causing this to fail that I can see.

1. Instagram's API only listens on HTTPS, not HTTP. The standard http Node module won't work here; you'll need to at least use https.

2. You're defining a variable called post_data inside a conditional statement:

if(headers['Content-Type'] == "application/json"){
    post_options["json"] = true;
    var post_data = JSON.stringify(body);
}else
    var post_data = querystring.stringify(body);

We already talked about not messing around with implied braces (DO NOT DO THIS), but beyond that, you're defining a local variable scoped only to the conditional statement and populating it with data. It'll be destroyed as soon as the conditional ends. You can check this by console.log(post_data) immediately after - it'll be empty.

3. There are three distinct steps to the Instagram OAuth flow - it looks like you're trying to (sort of?) proxy the first redirect URL? But you're also presenting the same URL for both of these, when it's actually two distinct endpoints. It also looks like you just copied the code from How to make an HTTP POST request in node.js? without quite understanding what it's doing or why. Most importantly, the Content-Type of the working curl (the Instagram sample code), using multipart/form-data, not x-www-form-urlencoded.


Solution

Since you actually haven't provided an MCVE, I can't really extrapolate from the broken code what you're trying to do. I can only guess, so I'm going to give you a solution that uses request to do the heavy lifting so you don't have to. You'll notice there is a dramatic reduction in code. Here are the steps it does:

  1. Generate an implicit grant link
  2. Create a server that listens for the redirection and captures the auth code
  3. Makes a POST request to Instagram to retrieve a token

Here you go:

const querystring = require('querystring'),
      http = require('http'),
      request = require('request'),
      url = require('url')

// A manual step - you need to go here in your browser
console.log('Open the following URL in your browser to authenticate and authorize your app:')
console.log('https://api.instagram.com/oauth/authorize/?' + querystring.stringify({
    client_id: "90b2ec5599c74517a8493dad7eff13de",
    redirect_uri: "http://localhost:8001",
    response_type: "code",
    scope: "public_content"
}))

// Create a server that listens for the OAuth redirect
http.createServer(function(req, res) {
    // Regrieve the query params from the redirect URI so we can get 'code'
    var queryData = url.parse(req.url, true).query || {}

    // Build form data for multipart/form-data POST request back to Instagram's API
    var formData = {
        client_id: "90b2ec5599c74517a8493dad7eff13de",
        client_secret: "1b74d347702048c0847d763b0e266def",
        grant_type: "authorization_code",
        redirect_uri: "http://localhost:8001",
        code: queryData.code || ""
    }

    // Send the POST request using 'request' module because why would you do it the hard way?
    request.post({
        uri: "https://api.instagram.com/oauth/access_token",
        formData: formData,
        json: true
    }, function(err, resp, body) {

        // Write the response
        console.log(body)
        res.setHeader('Content-Type', "application/json")
        res.end(JSON.stringify(body))
    })

}).listen(8001)
brandonscript
  • 68,675
  • 32
  • 163
  • 220