GitHub OAuth Authentication using Nodejs And Express

Hi guys! In this post I will show you how you can use OAuth with GitHub. We will be using Nodejs and Express framework for accomplishing this task.

Setting up the files and installing the dependencies:

First of all, let create all the files which we will be needing for this tutorial. Open the terminal and get to work:

$ mkdir github_oauth
$ cd github_oauth
$ touch app.js package.json config.js

Now open package.json and put in the following content:

{
  "name": "GitHubOAuth",
  "description": "A simple NodeJS app for authenticating to Github using OAuth.",
  "dependencies": {
    "httpdispatcher": "0.4.0",
    "request": "*"
  }
}

Make sure that you replace the name :)

Now it’s time to install the dependencies:

$ npm install

Fleshing out the structure of our app

The authentication flow is detailed beautifully in this help article. There are only two necessary endpoints which we need. The first one is /login which will redirect the user to GitHub to get the tokens and the second one is /callback which is the url GitHub will redirect the user to.

First of all go to GitHub and register a new application and save your credentials. Now add your credentials to the config.js file like this:

module.exports = {
	"client_id": "",
	"secret": ""
};

Now let’s add some imports to our app.js file.

//Lets require/import the HTTP module
var http = require('http');
var dispatcher = require('httpdispatcher');
var request = require('request');
var url = require('url');

//Configuration file [Not stored in Git]
var config = require('./config.js');

const HOST = 'localhost';
const PORT=8080; 

var GitHubConfig = {
  'client_id'   : config['client_id'],
  'secret'      : config['secret'],
  'redirect_uri': '',
  'scope'       : '',
  'state'       : Math.round(Math.random()*10)
}

//Route for static files' folder
dispatcher.setStatic('resources');

This will setup the basic structure of our app.

Routes

Now it’s time to setup the main / route.

// Get Homepage
dispatcher.onGet("/", function(req, res) {
    res.writeHead(200);
    res.write("<!DOCTYPE html><html><body>"+
    	"<style>body{text-align: center;}a{color: #000000;border: 1px solid black;padding: 10px 30px;text-decoration: none;margin-top: 10em;display: inline-block;font-size: 20px;transition: all .5s;}"+
    	"a:hover{background-color: black;color: white;}"+
    	"</style>"+
    	"<a href='login'>Login!</a></body></html>");
    res.end();
}); 

In the above route we simply respond to the request with a simple html response created on the fly. Now we need to implement the /login route which will redirect the user to GitHub for authentication:

//Redirect to login url on GitHub
dispatcher.onGet("/login", function(req, res) {
  var url = 'https://github.com/login/oauth/authorize'
  + '?client_id=' + GitHubConfig.client_id
  + (GitHubConfig.scope ? '&scope=' + GitHubConfig.scope : '')
  + '&state=' + GitHubConfig.state;

  res.setHeader('location', url);
  res.statusCode = 302;
  res.end();
});

Here we are constructing the url using our GitHub configuration and redirecting the user to the respective url.

Now we need to implement the /callback route where GitHub will redirect the user to.

//Get 'em callbacks!
dispatcher.onGet("/callback", function(req, res) {
  var query = url.parse(req.url, true).query;
  if (query.state == GitHubConfig.state){
    payload = {
      'code':       	query.code,
      'client_id':     	GitHubConfig.client_id,
      'client_secret': 	GitHubConfig.secret
    }
    console.log(payload);
    request.post({
      url: 'https://github.com/login/oauth/access_token', 
      formData: payload, 
      headers: {'Accept': 'application/json'}
      }, function (error, response, body) {
            if (!error && response.statusCode == 200) {
              var token = JSON.parse(body).access_token;
              res.statusCode = 302;
              authorized(res, token);
            }
      }
    );

  };
});

Now, this route seems a bit complex. Let’s deconstruct it step by step. First of all we extract the queries from the url. This includes everything after the ? in the url. Next we do a simple check query.state == GitHubConfig.state for fighting against CSRF attacks. After that we construct a payload which is just a fancy name for data sent with post request. Then we do a post request to GitHub and ask it to return us the token which we can use to make authenticated requests on behalf of the user.

Hey! Where is this authorized() function coming from? Well, we haven’t implemented it yet. Now is the best time to implement it :)

var authorized = function(res, token){
  request.get({
    url: "https://api.github.com/user", 
    headers: {'Authorization': 'token '+token, 'User-Agent': 'Mozilla/5.0'}}, 
    function(error, response, body) {
        if (!error && response.statusCode == 200) {
            body = JSON.parse(body);
            var user = body.login;
            res.end("<!DOCTYPE html><html><body>"+
            	"<style>.key{font-size:18px; color:blue;font-weight:bold;}.string,.number,.boolean,.null{font-size:18px;}</style>"+
            	"<pre>"+syntaxHighlight(JSON.stringify(body,null,2))+
            	"</pre></body></html>"
            );
        } else {
            console.log(body);
            res.end(body);
        }
    }
  );
};

In this function we do a simple get request to GitHub and ask it to return us the User data. We are also using a syntaxHighlight function which simply syntax highlights our json response:

var syntaxHighlight = function(json) {
    if (typeof json != 'string') {
         json = JSON.stringify(json, undefined, 2);
    }
    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
        var cls = 'number';
        if (/^"/.test(match)) {
            if (/:$/.test(match)) {
                cls = 'key';
            } else {
                cls = 'string';
            }
        } else if (/true|false/.test(match)) {
            cls = 'boolean';
        } else if (/null/.test(match)) {
            cls = 'null';
        }
        return '<span class="' + cls + '">' + match + '</span>';
    });
}

Our application is finished! Now we just need to add the initializing code.

Adding the initializing code

Just add the following code at the bottom of the app.js file:

//Create a server
var server = http.createServer(handleRequest);

//Lets start our server
server.listen(PORT, function(){
    //Callback triggered when server is successfully listening. Hurray!
    console.log("Server listening on: http://localhost:%s", PORT);
});

Now we are truly finished.

Running the app

Now you can easily run the app by typing in the following command in the terminal:

$ nodejs app.js

I hope you enjoyed the tutorial. I will see you later :)