Social Login with Twitter using hapi.js

This is another tutorial from the series about hapi. Today many applications have the need to provide social login functionality for the most important providers (Google, Facebook, Twitter, Github...). With hapi this is really easy to accomplish.

Our Application

We are going to build a very simple application, that implements a social login functionality using Twitter as an example.

It should give you an idea of just how simple this is using a hapi plugin called bell.

Getting started

Make sure you have one of the latest versions of Node installed on your machine (currently v6.2.1) as we use some of the new ES6 features.

Everything setup? Let's continue...

Start fast

If you want to skip the step by step copy and paste, just clone the Github Repository and follow the explanations. Otherwise just skip this section.

git clone https://github.com/p-meier/hapi-social-login.git  
cd hapi-social-login  
npm install  

Step 1: Create a Twitter application

Before we can actually start to code we have to create a Twitter application that will be granted by the user.

To do this goto apps.twitter.com, sign in and click on Create New App. Now fill out the form like this:

You can use all values as seen in the screenshot except the name of the application - is has to be unique and you have to think of your own. Scroll down, accept the Developer Agreement and click on Create your Twitter application.

You should see something similar to this. Switch to the tab called Keys and Access Tokens. There you find the Consumer Key and Consumer Secret which we will need later on...

Congratulations! You have successfully created your Twitter application for this tutorial. Now we can move on to the coding part.

Step 2: Create the application

This is the file structure of our application.

- node_modules/     // created by 'npm install'
- package.json      // dependencies and project info
- server.js         // entry point of app, define server
Node dependencies

Create a new file named package.json with this content:

{
  "name": "hapi-social-login",
  "version": "1.0.0",
  "description": "Simple project showing social login with hapijs and bell.",
  "main": "server.js",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "bell": "^7.9.0",
    "boom": "^3.1.1",
    "hapi": "^13.4.1",
    "hapi-auth-cookie": "^6.1.1"
  }
}

So let's look at the packages used:

  • hapi should be clear
  • boom is used for http friendly errors
  • bell handles all the OAuth handshake with Twitter for us and makes integration a breeze
  • hapi-auth-cookie provides a simple cookie-based session management

Now it is time to install our dependencies. Fire up your command line, navigate into the root directory of your app and type:

npm install  
Create the server

The implementation is so simple so that I will post the whole code here at once and explain it afterwards. Create a new file named server.js with this content:

'use strict';

const Hapi = require('hapi');  
const Boom = require('boom');

// Create a server with a host and port
const server = new Hapi.Server();  
server.connection({  
  port: 3000
});

// Register bell and hapi-auth-cookie with the server
server.register([require('hapi-auth-cookie'), require('bell')], function(err) {

  //Setup the session strategy
  server.auth.strategy('session', 'cookie', {
    password: 'secret_cookie_encryption_password', //Use something more secure in production
    redirectTo: '/auth/twitter', //If there is no session, redirect here
    isSecure: false //Should be set to true (which is the default) in production
  });

  //Setup the social Twitter login strategy
  server.auth.strategy('twitter', 'bell', {
    provider: 'twitter',
    password: 'secret_cookie_encryption_password', //Use something more secure in production
    clientId: 'Here goes the twitter Consumer Key',
    clientSecret: 'Here goes the twitter Consumer Secret',
    isSecure: false //Should be set to true (which is the default) in production
  });

  //Setup the routes (this could be done in an own file but for the sake of simplicity isn't)
  server.route({
    method: 'GET',
    path: '/auth/twitter',
    config: {
      auth: 'twitter', //<-- use our twitter strategy and let bell take over
      handler: function(request, reply) {

        if (!request.auth.isAuthenticated) {
          return reply(Boom.unauthorized('Authentication failed: ' + request.auth.error.message));
        }

        //Just store a part of the twitter profile information in the session as an example. You could do something
        //more useful here - like loading or setting up an account (social signup).
        const profile = request.auth.credentials.profile;

        request.cookieAuth.set({
          twitterId: profile.id,
          username: profile.username,
          displayName: profile.displayName
        });

        return reply.redirect('/');
      }
    }
  });

  server.route({
    method: 'GET',
    path: '/',
    config: {
      auth: 'session', //<-- require a session for this, so we have access to the twitter profile
      handler: function(request, reply) {

        //Return a message using the information from the session
        return reply('Hello, ' + request.auth.credentials.displayName + '!');
      }
    }
  });

  // Start the server
  server.start((err) => {

    if (err) {
      throw err;
    }

    console.log('Server running at:', server.info.uri);
  });
});

First to note is the code is heavily commented, so you should be able to understand it by just reading it. It is not exactly an useful application on it's own but serves as a playground for social login with hapi.

We register the plugins (bell and haps-auth-cookie) with the server. Then we setup two authentication strategies - one for a normal session and the other for the OAuth dance with Twitter. After that we create the routes and secure them with the right auth strategy. Finally we startup our server.

Important: To make the code work you have to copy the Consumer Key and Consumer Secret from your Twitter application and then add them to your version of the code in the twitter auth strategy.

Route /

This route requires a session setup and welcomes the authenticated user with his Twitter name.

If there is no session it will redirect to /auth/twitter...

Route /auth/twitter

This route handles the OAuth dance with Twitter. It redirects the user to Twitter and requests him to allow our application access to his profile data. This looks something similar to this:

After the user granted access he will be redirected back to our application where bell fetches his Twitter profile and provides it to us via request.auth.credentials.profile. We then store it in the session (if there is none it will be created) and redirect to / where the user will be welcomed with his Twitter name.

At this point you could also do something else than storing the credentials in the session. You could create a user account in your database or check if this user has already logged in with Twitter by querying your database. So you can create a multi user application for example. Other use cases are also possible.

Step 3: Testing in the Browser

Now start the server:

node server.js  

Fire up your Browser and navigate to http://localhost:3000. You should be redirected to authenticate with your Twitter account. After successfully granting our app you should see the welcome message:

If you do reload this page the message stays there because of the session.

Code

You can download the complete code of this tutorial on Github.


You want to learn more?

I just created a new online video course on Udemy and offer a 50% discount to loyal readers of my blog...

All you have to do is to click on the course image above. The discount code is included in the link!

I'm happy to see you in the course...


Patrick Meier

I am an entrepreneur and software developer, building scalable, distributed web systems with Java, NodeJs and AngularJs.

Weiden, Germany