Implement Passport.js authentication with Sails.js 1.0

Greg Hesp
Greg Hesp
Aug 25, 2017 · 3 min read

Step 1: Dependencies

Before we start, we need to ensure we have all the relevant dependencies installed into our Sails.js project

npm i --save bcrypt-nodejs
npm i --save passport
npm i --save passport-local
npm i --save jsonwebtoken

Step 2: Generate User Model

Now we need to generate the User Model. This will automatically create User.js within api/models.

sails generate model user

Within the User.js file, we can now configure this like so:

const bcrypt = require('bcrypt-nodejs');module.exports = {attributes: {
email: {
type: 'email',
required: true,
unique: true
},
username: {
type: 'string',
required: true,
unique: true
},
password: {
type: 'string',
required: true
}
},
customToJSON: function() {
return _.omit(this, ['password'])
},
beforeCreate: function(user, cb){
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(user.password, salt, null, function(err, hash){
if(err) return cb(err);
user.password = hash;
return cb();
});
});
}
};

For the moment, we will only user email, username and password as the user attributes. To ensure we don’t pass the password back when searching the User model, the customToJSON function removes it.

In addition, we use bcrypt to hash the users password before storing it in our database

Step 3: Generate Auth Controller

Now we have our User model configured, we need to add a controller to handle authorisations. Run the following to generate an Auth Controller

sails generate controller auth

This will create a file called AuthController.js within api/controllers. Open this file and configure it as so:

const passport = require('passport');module.exports = {login: function(req, res) {
passport.authenticate('local', function(err, user, info){
if((err) || (!user)) {
return res.send({
message: info.message,
user
});
}
req.logIn(user, function(err) {
if(err) res.send(err);
return res.send({
message: info.message,
user
});
});
})(req, res);
},
logout: function(req, res) {
req.logout();
res.redirect('/');
}
};

Step 4: Create the Views

We need somewhere for the user to login and register, so for this we’re going to create a couple of views

Create 2 files within the views folder called register.ejs and login.ejs. Set them up with a simple form like so:

register.ejs

<h1>Register</h1>
<form method="POST" action="/user">
<input type="text" name="username" placeholder="Username">
<input type="email" name="email" placeholder="Email Address">
<input type="password" name="password" placeholder="Password">
<input type="submit" value="submit">
</form>

login.ejs

<h1>Login</h1>
<form method="POST" action="/login">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<input type="submit" value="submit">
</form>

Step 5: Configuring Routes

To make sure our users can register and login, we need to configure the Sails routing.
Open the config/routes.js file and add in the following lines:

'GET /login': { view: 'login' },
'POST /login': 'AuthController.login',
'/logout': 'AuthController.logout',
'GET /register': { view: 'register' }

Step 6: Configuring Middleware

To ensure Passport.js functions, we need to configure the Middleware in Sails to accept it. Open up the config/http.js file, and within the middleware section add the following lines:

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

order: [
'cookieParser',
'session',
'passportInit',
'passportSession',
'bodyParser',
'compress',
'poweredBy',
'router',
'www',
'favicon',
],

Step 7: Define the Passport Strategy

This is where we define the different Passport Strategies. For now, we’re only interested in the Passport-Local strategy, however this file can be configured to accept any of the various Passport Strategies available.

Within your config folder, create a new file called passport.js and configure it like so:

const passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
bcrypt = require('bcrypt-nodejs');
passport.serializeUser(function(user, cb) {
cb(null, user.id);
});
passport.deserializeUser(function(id, cb){
User.findOne({id}, function(err, users) {
cb(err, users);
});
});
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
}, function(username, password, cb){
User.findOne({username: username}, function(err, user){
if(err) return cb(err);
if(!user) return cb(null, false, {message: 'Username not found'});
bcrypt.compare(password, user.password, function(err, res){
if(!res) return cb(null, false, { message: 'Invalid Password' });
let userDetails = {
email: user.email,
username: user.username,
id: user.id
};
return cb(null, userDetails, { message: 'Login Succesful'});
});
});
});

Step 8: Results

Now when a user registers, you should see an entry in your database that looks like:

{
"_id": {
"$oid": "59a004e57da8856b947d6d46"
},
"username": "JohnDoe",
"email": "john.doe@example.com",
"password": "$2a$10$wi5lfT5dBh.dH9X4DhU0u.cMzOEr2R8b2mMsY.wuwSh2FOH1..WKW",
"createdAt": 1503659237714,
"updatedAt": 1503659237714
}

In addition, the user model will be returned in JSON format with the password removed:

{
createdAt: 1503659237714,
updatedAt: 1503659237714,
id: "59a004e57da8856b947d6d46",
email: "john.doe@example.com",
username: "JohnDoe"
}

)

Greg Hesp

Written by

Greg Hesp

I’m always doing “stuff” and forgetting how I did it. So here’s my “stuff” for everyone to enjoy

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade