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
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](mailto:mail2@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](mailto: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](mailto:mail2@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](mailto: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](mailto: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*.