sailsjs, passportjs, nodejss

Step 1: dependencies Add these dependencies to the yourproject/package.json file:

...
  "dependencies": {
    ...
    "bcrypt": "~0.8.0",
    "passport": "~0.2.1",
    "passport-local": "~1.0.0",
    ...
   }
... 

Open the terminal, go to your project folder and run:

[sudo] npm install

Step 2: create user model To create a user model run the following command:

sails generate api user

This will automatically create a model and a controller. You can find the model at yourproject/api/models/User.js. Let’s make that file look like this:

var bcrypt = require('bcrypt');

module.exports = {
    attributes: {
        email: {
            type: 'email',
            required: true,
            unique: true
        },
        password: {
            type: 'string',
            minLength: 6,
            required: true
        },
        toJSON: function() {
            var obj = this.toObject();
            delete obj.password;
            return obj;
        }
    },
    beforeCreate: function(user, cb) {
        bcrypt.genSalt(10, function(err, salt) {
            bcrypt.hash(user.password, salt, function(err, hash) {
                if (err) {
                    console.log(err);
                    cb(err);
                } else {
                    user.password = hash;
                    cb();
                }
            });
        });
    }
};

Step 3: create AuthController Run this command from your terminal: sails generate controller auth

Then go to yourproject/api/controllers/AuthController.js and alter it in this way:

var passport = require('passport');

module.exports = {

    _config: {
        actions: false,
        shortcuts: false,
        rest: false
    },

    login: function(req, res) {

        passport.authenticate('local', function(err, user, info) {
            if ((err) || (!user)) {
                return res.send({
                    message: info.message,
                    user: user
                });
            }
            req.logIn(user, function(err) {
                if (err) res.send(err);
                return res.send({
                    message: info.message,
                    user: user
                });
            });

        })(req, res);
    },

    logout: function(req, res) {
        req.logout();
        res.redirect('/');
    }
};

Step 4: create login and signup views Create this file yourproject/views/signup.ejs:

<h1>Signup</h1>
<form method="POST" action="/user">
  <input type="email" name="email">
  <input type="password" name="password">
  <input type="submit" value="submit">
</form>

And then this yourproject/views/login.ejs:

<h1>Login</h1>
  <form method="POST" action="/login">
  <input type="email" name="email">
  <input type="password" name="password">
  <input type="submit" value="submit">
</form>

These forms look the same, besides the action.

Step 5: configuring the routes Let’s set up our routes in yourproject/config/routes.js like this:

module.exports.routes = {
  '/': {
    view: 'homepage'
  },

  'get /login': {
       view: 'login'
  },

  'post /login': 'AuthController.login',

  '/logout': 'AuthController.logout',

  'get /signup': {
    view: 'signup'
  }
};

If you notice we are leveraging HTTP verbs, we have two login actions but different routes.

Step 6: configuring Express Middleware Since we are using Passport we have to customize our middleware in yourproject/config/http.js:

module.exports.http = {
   middleware: {

    passportInit    : require('passport').initialize(),
    passportSession : require('passport').session(),

     order: [
            'startRequestTimer',
            'cookieParser',
            'session',
            'passportInit',     
            'passportSession', 
            'myRequestLogger',
            'bodyParser',
            'handleBodyParserError',
            'compress',
            'methodOverride',
            'poweredBy',
            'router',
            'www',
            'favicon',
            '404',
            '500'
          ],
     }
};

Into the order array we added passportInit and passportSession after the session element. The name of this array is quite self-explanatory, the elements must be ordered this way.

Step 7: define Passport local strategy for authentication Into yourproject/config/ folder let’s create a new file called passport.js:

var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
bcrypt = require('bcrypt');

passport.serializeUser(function(user, done) {
    done(null, user.id);
});

passport.deserializeUser(function(id, done) {
    User.findOne({ id: id } , function (err, user) {
        done(err, user);
    });
});

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password'
  },
  function(email, password, done) {

    User.findOne({ email: email }, function (err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect email.' });
      }

      bcrypt.compare(password, user.password, function (err, res) {
          if (!res)
            return done(null, false, {
              message: 'Invalid Password'
            });
          var returnUser = {
            email: user.email,
            createdAt: user.createdAt,
            id: user.id
          };
          return done(null, returnUser, {
            message: 'Logged In Successfully'
          });
        });
    });
  }
));

Step 8: add a new policy Let’s create a new policy isAuthenticated.js into the yourproject/api/policies/ folder:

module.exports = function(req, res, next) {
   if (req.isAuthenticated()) {
        return next();
    }
    else{
        return res.redirect('/login');
    }
};

Briefly, we are telling Sails what to do if a user is not authenticated, then we can bind this policy to any of the controllers in the app.

Step 9: bind the policy to a controller Let’s create a controller and a model just for testing what we have done. I am going to call this api post.

sails generate api post

Then let’s bind the policy to the PostController by updating yourproject/config/policies.js:

module.exports.policies = {

   '*': true,

  'PostController': {
    '*': 'isAuthenticated'
  },

};

Now the ‘GET /post’ route (and all the other routes automatically generated for this controller ) is available only to authenticated users.

Step 10: run a test Lift your application sails lift And then go and play with these routes:

http://localhost:1337/post - the very first time it should redirect you to the login page

go to http://localhost:1337/signup for signing up

go to http://localhost:1337/login for logging in

now you should be able to access this page http://localhost:1337/post, even though it’s going to show you just an empty array (there aren’t any posts yet)

go to http://localhost:1337/logout for logging out

→ Get the repository on Github

I would very much appreciate your comments, especially if you find an error or you need a further explanation. References:

→ Sails.js → Passport.js → Waterline

When in doubt, use brute force. — Ken Thompson

“If you are going to walk on thin ice, you might as well dance”