Node.js Authentication using Passport.js

Shams Nahid

Check the source code here

Authentication is an important portion for web application. From client side, like a mobile application must require a secure authentication to the server and passport is a prominent helper.

Here I’m developing a simple oAuth(open authentication) application and show how to secure routing.

Work-Flow

  • Two routes will be available: guest, user
  • The /guest routes can be accessed by anyone
  • The /user routes can be accessed by the authenticate users

REQUIREMENTS

  1. Node.js(6.x)
  2. NPM(3.x)
  3. Express.js(4.x)

APP SETUP

Dependency

Make your directory and inside the directory, create a file named package.json

mkdir myPassportAuthentication
cd myPassportAuthentication/
touch package.json

Paste the following code in your package.json file:

{
"name": "my-passport-authentication",
"main": "server.js",
"dependencies" : {
"express": "~4.14.0",
"mongoose": "~4.6.1",
"passport": "~0.3.2",
"passport-local" : "~1.0.0",
"bcrypt-nodejs" : "latest",
"morgan": "~1.7.0",
"body-parser": "~1.15.2",
"cookie-parser": "~1.4.3",
"method-override": "~2.3.6",
"express-session": "~1.14.1"
}
}

To download the dependencies, run

npm install

So our entry point is the server.js

Server

Lets create the server.js in the same directory.

touch server.js

Now paste the following code in server.js

// server.js

// set up ======================================================================
// get all the tools we need
var express = require('express');
var app = express();
var path = require('path');
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');

var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');

// router files ===============================================================
var authRoutes = require('./routes/auth');
var testRoutes = require('./routes/test');

var configDB = require('./config/database');

// configuration ===============================================================
mongoose.connect(configDB.url); // connect to our database

require('./config/passport')(passport); // pass passport for configuration

// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.json()); // get information from html forms
app.use(bodyParser.urlencoded({ extended: true }));

// required for passport
app.use(session({
secret: 'eminem', // session secret
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions

// routes ======================================================================
app.use('/auth', authRoutes);
app.use('/test', testRoutes);

// launch ======================================================================
app.listen(port);
console.log('The magic happens on port ' + port);

Routes

Create two routing files,

mkdir routes
cd routes
mkdir auth
mkdir test
cd auth
touch index.js
cd ..
cd test
touch index.js

From the both routing file

  • ./routes/auth/index.js
  • ./routes/test/index.js

Export the router module.

Paste the following code on the both file:

var express = require('express');
var
router = express.Router();
module.exports = router;

Database

Now time to configure database. Follow the following commands:

mongo
use myPassportAuth

So our database is created, now create the config file and put database info:

Go to the project root directory and

mkdir config
cd config
mkdir database
touch index.js

Inside the ./config/database/index.js paste the following code:

// config/database.js
module.exports = {
'url' : 'mongodb://localhost/myPassportAuth'
};

Passport

Go to the root directory and follow the command:

cd config
mkdir passport
cd passport
touch index.js

Now paste the following code to the ./config/passport/index.js

// load all the things we need
var LocalStrategy = require('passport-local').Strategy;

var User = require('../../models/user');

var myLocalConfig = (passport) => {
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session

// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});

// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});

// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
if (email)
email = email.toLowerCase(); // Use lower-case e-mails to avoid case-sensitive e-mail matching

// asynchronous
process.nextTick(function() {
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);

// if no user is found, return the message
if (!user)
return done(null, false);

if (!user.validPassword(password))
return done(null, false);

// all is well, return user
else
return
done(null, user);
});
});

}));

// =========================================================================
// LOCAL SIGNUP ============================================================
// =========================================================================
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
if (email)
email = email.toLowerCase(); // Use lower-case e-mails to avoid case-sensitive e-mail matching

// asynchronous
process.nextTick(function() {
// if the user is not already logged in:
if (!req.user) {
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);

// check to see if theres already a user with that email
if (user) {
return done(null, false);
} else {

// create the user
var newUser = new User();

newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);

newUser.save(function(err) {
if (err)
return done(err);

return done(null, newUser);
});
}

});
// if the user is logged in but has no local account...
} else if ( !req.user.local.email ) {
// ...presumably they're trying to connect a local account
// BUT let's check if the email used to connect a local account is being used by another user
User.findOne({ 'local.email' : email }, function(err, user) {
if (err)
return done(err);

if (user) {
return done(null, false);
// Using 'loginMessage instead of signupMessage because it's used by /connect/local'
} else {
var user = req.user;
user.local.email = email;
user.local.password = user.generateHash(password);
user.save(function (err) {
if (err)
return done(err);

return done(null,user);
});
}
});
} else {
// user is logged in and already has a local account. Ignore signup. (You should log out before trying to create a new account, user!)
return done(null, req.user);
}

});

}));
};

module.exports = myLocalConfig;

Model

Again go to the root directory and follow the commands

mkdir models
cd models
mkdir user
cd user
touch index.js

Paste the following code and make the user model:

// load the things we need
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');

// define the schema for our Photographer model
var userSchema = mongoose.Schema({

local : {
email : String,
password : String
}
});

// generating a hash
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

// checking if password is valid
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};

// create the model for photographers and expose it to our app
module.exports = mongoose.model('User', userSchema);

Boilerplate Completion

Here’s our boilerplate code is ready. Go to the root directory and run

node server.js

You must see: “The magic happens on port 8080”

Let’s complete the auth route. Paste the code in ./routes/auth/index.js

var express = require('express');
var router = express.Router();
var passport = require('passport');

router.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/auth/profile',
failureRedirect : 'auth/signup'
}));

router.post('/login', passport.authenticate('local-login', {
successRedirect : '/auth/profile',
failureRedirect : 'auth/login'
}));
router.get('/profile', isLoggedIn, (req, res) => {
res.status(200).json(req.user);
});
router.get('/logout', isLoggedIn, (req, res) => {
req.logout();
res.status(200).json({
'message': 'successfully logout'
});
});

module.exports = router;

//route middleware to ensure user is logged in
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.status(400).json({
'message': 'access denied'
});
}

So we are done for signIn, signOut, SignUp and viewProfile Section.

Secure Routing

Here we create two routing, one for the guest and another for the authenticated users.

Go to the ./routes/test/index.js and it would be like this:

var express = require('express');
var router = express.Router();

router.get('/guest', (req, res) => {
res.status(200).json({
'message': "This is not secured guest routing"
});
});

router.get('/user', isLoggedIn, (req, res) => {
res.status(200).json({
'message': "This is secured user routing"
});
});

module.exports = router;

//route middleware to ensure user is logged in
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.status(400).json({
'message': 'access denied'
});
}

Congratulation

If you have come this far, then congratulation, you implements the basic open authorization using passport js and secure specific routing.

API Test

We will use postman to check the API.

Create an user:

Request Type: POST

Headers: { ‘content-type’: ‘application/json’ }

Address: localhost:8080/auth/signup

Body: raw


{
“email”: “test@mail.com”,
“password”: “1234567890”
}

In response we will get user id and hashed password:

{
“_id”: “59c7d5f7294b322b0321ad6a”,
“__v”: 0,
“local”: {
“password”: “$2a$08$varXSszNbVsFH85B3Ewk4.Q4qhXRJBeyDrZNLu7vrlJhoBefpc50S”,
“email”: “test@mail.com
}
}

When we create an user we will be automatically logged in.

Now Let say, A user already exist and wants to logged in,

Log in an User:

Request Type: POST

Headers: { ‘content-type’: ‘application/json’ }

Address: localhost:8080/auth/login

Body: raw

{
“email”: “test@mail.com”,
“password”: “1234567890”
}

In response we will get Logged in user information:

{
“_id”: “59c7d5f7294b322b0321ad6a”,
“__v”: 0,
“local”: {
“password”: “$2a$08$varXSszNbVsFH85B3Ewk4.Q4qhXRJBeyDrZNLu7vrlJhoBefpc50S”,
“email”: “test@mail.com
}
}

Profile of a logged in user:

To get the information of a logged in user,

Request Type: GET

Address: localhost:8080/auth/profile

In response we will get the users profile:

{
“_id”: “59c7d5f7294b322b0321ad6a”,
“__v”: 0,
“local”: {
“password”: “$2a$08$varXSszNbVsFH85B3Ewk4.Q4qhXRJBeyDrZNLu7vrlJhoBefpc50S”,
“email”: “test@mail.com
}
}

Log out of an user:

To log out from the app,

Request Type: GET

Address: localhost:8080/auth/logout

In response we will get the message:

{
"message": "successfully logout"
}

Open authorization is completed here.

Securing Routing

When user is not logged in,

Request Type: GET

Address: localhost:8080/test/user

{
“message”: “access denied”
}

Request Type: GET

Address: localhost:8080/test/guest

{
“message”: “This is not secured guest routing”
}

When user is logged in,

Request Type: GET

Address: localhost:8080/test/user

{
“message”: “access denied”
}

Request Type: GET

Address: localhost:8080/test/guest

{
“message”: “This is not secured guest routing”
}

Conclusion

So Here we are, completed the basic open authentication using passport.js in node.js and express.js server. In future I will go through authentication using facebook, google, twitter and like other stuff. Stay tuned and if there is a confusing term or something, response below or create an issue in github.

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