An Ode to OAuth

I lied! Not an ode:
just a mere haiku or two
detailing OAuth.

I spent a whole day
paired with a clever friend on
trial and error.

Such a simple task:
download shots from Instagram.
Not easy at all!

Step One: REDIRECT
to Instagram’s own login.
On success, return.

From login response,
“GET” client’s code, build request:
H-T-T-P-S!

Add secret dev codes,
POST to port 4-4-3 with
concat-ed path name.

If you’re deemed worthy,
you’ll receive JSON response;
else days of headaches.

end poetry;

Using OAuth & Node to Access Instagram’s API

… quite a simple task, as it turns out, but nowhere could we find the exact sequence of delicate moves to make the magic happen. So here, for all future generations, I put it forth:

Assumed: you’ve got a node.js server up and running, likely using some handy library like Express (if not, no biggie, just see one of a thousand tutorials or perhaps a future blog post of mine); you’ve registered as a developer with Instagram (it’s super-simple: go to instagram.com/developer/, register your app & such). NOTE: Instagram WORKS with your redirect uri set to “http://localhost:3000” or whatever port you’re running on your local computer’s test server. That shocked the heck outta me, but it’s fine. Go figure and rejoice! From registration, you’ll need to take note of your super-secret client_id (a misnomer, really, as in only this case are you the ‘client’ insofar as you are requesting data from their servers — confusing because you think of your users as clients, bah!), and your super-super-secret client_secret (very secret!).

Once you’ve got that jazz, build a little link or page or something that a user can arrive on and click “go” or something to be redirected to Instagram to log in to their account (it is actually very handy that Instagram handles that stuff for us!). The url you’ll want to use for that link is:

"https://api.instagram.com/oauth/authorize/?client_id=" + client_id + "&redirect_uri=" + whateverYouSetAsYourRedirectURIwithInstagram + "&response_type=code"

Note: If you’re testing on your computer, your redirect_uri (whateverYouSetAsYourRedirectURIwithInstagram) might look like “http://localhost:3000”, assuming you’re running your node server on port 3000.

Next, user will click that link, agree to give you their soul under Instagram’s oversight, then be redirected back to… your redirect link! Hopefully you gave Instagram a link to something on your server and you’ve set up your node server to handle that response. I did, so I’m cruisin’ happily at this point.

Great! User agreed and is back in your hands, probably at a url on your server that looks like “redirectURI?code=12345678901234567”, where redirectURI in my case was “http://localhost:3000/home” and code was some frequently-changing garble of many numbers. This is a “GET” request to your server, and the “?code=” is a parameter name, complete with a value (the bunch of numbers). You’ll want to grab that pile o’ numbers. You need it for your next request. (I know, right? ANOTHER request?!? yes. sorry.)

[For the cheaters: Node.js makes it super-simple to grab that code from the url. You can include a built-in node module called “url” and use it to parse that huge url for you and give you the bits you care about. Here’s how:]

var urlParser = require('url');
// "require" node module called url
...

app.get('/home', function (req, res, callback) {
// somewhere inside your function that handles the "GET" request to your redirectURI page:
    ...
    var userCode = urlParser.parse(req.url).query.toString().replace(/code=/, ''); 
    // parse the requested url, pull out the query piece, make sure it's a string, strip off the "code=" part
    ...
};

Now you’re ready for the big league: REQUEST FOR ACCESS_TOKEN. In the Instagram API documentation, they say “POST a request to their server” and show an example using CURL. If you’re coding in php or accessing data directly from your terminal or some shit, that’s great for you. If you want to fuss with a node-curl library just to do that through node, also have fun. I was not in either of those situations, and I KNEW there was a way to send an HTTPS POST to another server from my node.js server. I just KNEW it. (Okay, so I had read a lot of the Node documentation, and I did actually know it — not just some gastrointestinal hunch, unfortunately.) Anyway, it’s true, but the Node examples do not line up well with what you need to do in this situation. Here’s what has to happen:

var https = require('https');
// require 'https' module from node

var querystring = require('querystring');
// require 'querystring' module from node [NOTE: this is DIFFERENT than JSON.stringify!]

var sendData = querystring.stringify({ // build data object to send and turn it into a querystring
    'client_id' : YourClient_id, // given to you by Instagram as a developer
    'client_secret' : YourClient_secret, // given to you by Instagram as a developer, very secret!
    'grant_type' : 'authorization_code', // just type both of these strings literally
    'redirect_uri' : YourRedirectURI, // the one you set with Instagram
    'code' : userCode // the one we fetched from Instagram's first GET response above
});

var postOptions = {
    hostname: 'api.instagram.com',
    port: 443,
    method: 'POST',
    path: '/oauth/access_token',
    headers: {
        'Content-Length': sendData.length // get length of the data string you are sending
    }
};

var request = https.request(postOptions, function (response) { // prepare our request
    var receivedData = ''; // create a new, empty place to catch our response data
    response.setEncoding('utf8'); // if you don't set this, you get back a buffer of junk
    response.on('data', function (chunk) { // respond to the 'data' event by catching each chunk of data
        receivedData += chunk; // and adding it to our empty response basket
    });
    response.on('end', function () { // respond to the 'end' event
        receivedData = JSON.parse(receivedData); // by parsing the JSON we (hopefully) received
        console.log(receivedData); // for now, let's just log this to make sure it's working
    });
}).on('error', function (e) { // catch request problems
    console.log('https POST request error: ' + e); // log the error with a reminder to yourself
});
request.write(sendData); // ACTUALLY write the freakin' request object
request.end(); // send that sucker!

With any luck, this WILL work, and you should receive a JSON object that looks like this:

{ access_token: '1234567.8a90bcd.12ef3g4567890hijkl123m45678n90o1', // I made this up
    user: { 
        username: 'username',
        bio: 'the bio your user wrote',
        website: 'www.youruserswebsite.com',
        profile_picture: 'http://images.ak.instagram.com/profiles/profile_1234567_89ab_0123456789.jpg',
        full_name: 'User Name',
        id: '1234567' 
    } 
}

Now you can use that GOLDEN ACCESS_TOKEN to get ANY DATA YOU WANT! It’s amazing! And beyond that, Instagram’s API is quite lovely to work with. They use Apigee, and requests to the API are fairly simple and self-explanatory (or rather, their documentation about that is great).

That’s all for tonight, folks! Feel free to post your tales of joy or woe in working with OAuth. I’d love to hear that I’m not the only one who’s lost a day to those nitty-gritty implementation details…

Also, much credit owed to my fellow student and friend, N, who worked through this mess alongside me. We’ve learned so much from this yet-unfinished “simple” project. Stay tuned!

Leave a Reply

Your email address will not be published. Required fields are marked *