
Implement Passport.js authentication with Sails.js 1.0
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 jsonwebtokenStep 2: Generate User Model
Now we need to generate the User Model. This will automatically create User.js within api/models.
sails generate model userWithin 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 authThis 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"
}