User authentication with Hapi, Hapi-Auth-Cookie and Mongoose

On march 4th, 2015 I have updated the code to be compatible with Hapi 8.2. Some minor changes have been made. Most of the stuff below is still valid. The commits can be found on the commit overview.

This is the updated version of User authentication with Hapi, Passport and Mongoose. The reason for this update is that Travelogue, which we used there, is being discontinued. The fact it's being discontinued also meant I was unable to update the dependencies (Hapi from 3 to 6, for example) and made it quite useless.

Eran Hammer tipped Hapi-auth-cookie as a replacement for what I required. Bell doesn't allow local strategies so that wasn't an option.

Alright, a short overview of the changes.

  1. Remove, update and add packages
  2. Clean index.js
  3. Add Hapi-auth-cookie to index.js
  4. Update POST /login

1. Remove, update and add packages

First we remove the old packages (Travelogue, Passport and Passport-local & Yar)
Simply do: npm uninstall --save travelogue passport passport-local yar to remove them. If it doesn't work, delete the node_modules folder and start fresh.

Second we add hapi-auth-cookie: npm install --save hapi-auth-cookie

Third, update the old packages: npm install --save hapi@6.2.x joi@4.6.x mongoose@3.8.13

Or combined if you're feeling bold: npm uninstall --save travelogue passport passport-local yar && npm install --save hapi-auth-cookie hapi@6.2.x joi@4.6.x mongoose@3.8.13

Or updating package.json manually:

  "dependencies": {
    "mongoose": "~3.8.13",
    "joi": "~4.6.x",
    "hapi": "~6.2.x",
    "hapi-auth-cookie": "~1.3.1",
    "passport-local-mongoose": "~0.3.0"
  }

2. Clean index.js

You can remove everything between the Travelogue comments, some of the other parts will be moved. If your code looked anything like mine, you can remove all of it.

3. Add Hapi-auth-cookie to index.js

Index.js now looks like:

var Hapi = require('hapi');  
var Routes = require('./routes');  
var Config = require('./config');  
var User = require('./models/user').User;

// Create a server with a host and port
var server = Hapi.createServer(Config.server.port);

// Register the plugin
server.pack.register(require('hapi-auth-cookie'), function (err) {  
    if (err) {
        throw err;
    }

    // Set our strategy
    server.auth.strategy('session', 'cookie', {
        password: 'worldofwalmart', // cookie secret
        cookie: 'session', // Cookie name
        redirectTo: false, // Let's handle our own redirections
        isSecure: false, // required for non-https applications
        ttl: 24* 60 * 60 * 1000 // Set session to 1 day
    });

    // Print some information about the incoming request for debugging purposes
    server.ext('onRequest', function (request, next) {
        console.log(request.path, request.query);
        next();
    });

    server.route(Routes.endpoints);

    // Start the server
    server.start(function() {
        console.log("The server has started on port: " + server.info.port);
    });
});

4. Update POST /login

Considering we no longer use Passport and that POST /login depends on it, we'll have to remove it and write our own. An example looks like:

exports.login = {  
    validate: {
        payload: {
            email: Joi.string().email().required(),
            password: Joi.string().required()
        }
    },
    handler: function (request, reply) {

        // In the version with Travelogue and Mongoose this was all handled by Passport (hence we retrieved
        // Passport and inserted the request and reply variables).
        User.authenticate()(request.payload.email, request.payload.password, function (err, user, message) {

            // There has been an error, do something with it. I just print it to console for demo purposes.
            if (err) {
                console.error(err);
                return reply.redirect('/login');
            }

            // If the authentication failed user will be false. If it's not false, we store the user
            // in our session and redirect the user to the hideout
            if (user) {
                request.auth.session.set(user);
                return reply.redirect('/batmanshideout');
            }
            return reply(message);

        });
    }
};

That, and setting auth: 'passport' to auth: 'session' is all you have to change. Take a look at the repo for more info.

comments powered by Disqus