New authentication providers: LinkedIn and GitHub

Backbeam now supports two new authentication providers: LinkedIn and GitHub. The documentation for all SDKs has been updated.

In this article you are going to see an example of how to integrate both authentication providers. We are going to implement both at the same time! They are very similar since both use Oauth2.

Let's create a controller that implements the first step in the OAuth2 authentication process: generate a URL to ask the user for permissions.

var state = require('utilities').nonce()
session.set('state', state) // remember state

function createLinkedInURL() {
    var host = request.headers.host
    var params = {
        'response_type':'code',
        'client_id': 'your-linkedin-client-id',
        'scope': 'r_basicprofile', // see https://developer.linkedin.com/documents/authentication#granting
        'state': state,
        'redirect_uri': 'http://'+host+'/signup-linkedin'
    }
    return 'https://www.linkedin.com/uas/oauth2/authorization?'+backbeam.util.querystring.stringify(params)
}

// For GitHub it is very important to have the redirect_uri match the
// callback URL configuration setting in your application settings.
// See https://developer.github.com/v3/oauth/#redirect-urls
function createGitHubURL() {
    var host = request.headers.host
    var params = {
        'client_id': 'your-github-client-id',
        'scope': '', // see https://developer.github.com/v3/oauth/#scopes
        'state': state,
        'redirect_uri': 'http://'+host+'/signup-github'
    }
    return 'https://github.com/login/oauth/authorize?'+backbeam.util.querystring.stringify(params)
}

var options = {
    linkedInURL: createLinkedInURL(),
    gitHubURL: createGitHubURL(),
}
response.render('index.html', options, true)

This controller could be placed at GET / for example. It renders a index.html. Its minimal version should be something like this:

<p><a href="{{ linkedInURL }}">Sign in with LinkedIn</a></p>

<p><a href="{{ gitHubURL }}">Sign in with GitHub</a></p>

Now we need two controllers more. As you can see in the first controller OAuth2 needs a redirect_uri that we need to implmenet. We need to create two controllers for them: GET /signup-linkedin and GET /signup-github. This is the first one:

var host = request.headers.host

var code = request.query.code
var state = request.query.state

if (code && state) {
    if (state !== session.get('state')) {
        respone.send('Invalid state parameter!')
    } else {
        var options = {
            url: 'https://www.linkedin.com/uas/oauth2/accessToken',
            qs: {
                grant_type: 'authorization_code',
                code: code,
                redirect_uri: 'http://'+host+'/signup-linkedin',
                client_id: 'your-linkedin-client-id',
                client_secret: 'your-linkedin-client-secret'
            }
        }
        var http = backbeam.httpClient()
        http.get(options, function(err, res) {
            if (err) throw new Error(err)
            var json = JSON.parse(res.body)
            var accessToken = json['access_token']
            if (accessToken) {
                var credentials = {
                    access_token: accessToken
                }
                backbeam.linkedInSignup(credentials, function(err, user) {
                    if (err) {
                        response.send(err.name)
                    } else {
                        // TODO: you should redirect here with response.redirect('...')
                        response.send('Hello '+user.getLinkedInData('first_name')+' '+user.getLinkedInData('last_name')+' ('+user.getLinkedInData('headline')+')')
                    }
                })
            } else {
                response.send('Login error '+res.body)
            }
        })
    }
} else {
    throw new Error()
}

Once the user grants access to our application it redirects to this URL passing us a code and a state. We verify that the state is the same we sent in the first step and then we call LinkedIn to finally get the access_token of the user. LinkedIn responds with a JSON response. The last step is, having the access_token, signup the user into Backbeam. And that's it! Backbeam stores a few attributes of the user like you can see such as the first-name, last-name, headline and id (LinkedIn's id for that user).

Implementing this step with GitHub is similar. Let's see the code. You need a GET /signup-github controller:

var host = request.headers.host

var code = request.query.code
var state = request.query.state

if (code && state) {
    if (state !== session.get('state')) {
        respone.send('Invalid state parameter!')
    } else {
        var options = {
            url: 'https://github.com/login/oauth/access_token',
            qs: {
                code: code,
                redirect_uri: 'http://'+host+'/signup-github',
                client_id: 'your-github-client-id',
                client_secret: 'your-github-client-secret'
            }
        }
        var http = backbeam.httpClient()
        http.get(options, function(err, res) {
            if (err) throw new Error(err)
            var params = backbeam.util.querystring.parse(res.body)
            var accessToken = params['access_token']
            if (accessToken) {
                var credentials = {
                    access_token: accessToken
                }
                backbeam.gitHubSignup(credentials, function(err, user) {
                    if (err) {
                        response.send(err.name)
                    } else {
                        // TODO: you should redirect here with response.redirect('...')
                        response.send('Hello '+user.getGitHubData('login')+' '+user.getGitHubData('name'))
                    }
                })
            } else {
                response.send('Login error '+res.body)
            }
        })
    }
} else {
    throw new Error()
}

You can see that the code is almost the same. The main differences is that both platforms require slightly different parameters and that in this case GitHub responds with a url-encoded string to get the access token instead of sending JSON like LinkedIn.

By the way. In the source code we are using a utilities.js library with a method that returns a random token. The souce code of that library could be something like this:

var chars = toArray('ABCDEFGHIJKLMNOPQRSTUVWXYZ')

exports.nonce = function() {
    var arr = []
    for (var i = 0; i < 42; i++) {
        arr.push(chars[Math.floor(Math.random() * chars.length)])
    }
    return arr.join('')
}

function toArray(str) {
    var arr = []
    for (var i = 0; i < str.length; i++) arr[i] = str.charAt(i)
    return arr
}

We hope you liked this new functionality!