USER AUTHENTICATION USING PASSPORT-JWT STRATEGY IN NODEJS.

This article is written to help developers write applications with user authentication using node and two of its packages. We will discuss the usage of “Passport” and “bcrypt” packages as middleware for programming user authentication using Node.js. The authentication middleware which we’ll examine is the Passport package since it works well in Node.js “Express” package based applications, allowing users to log in with username and password. The usage of passport for username and password verification with a self-generated JSON Web Token(JWT). Passport can generate, extract, and validate these web tokens with an expiration date and data for checking users. The password hashing middleware which we use to compliment Passport’s functionality is the Bcrypt package. This tool allows us to save the user in the database to later compare with the password (which should be encrypted) when generating their authentication token. Make sure to install all the following packages to your dependencies: “express”, “body-parser”, “express”, “passport”, “bcrypt”, “cookie-parser”, “Jsonwebtoken”, “mysql2”, “sequelize”, “sequelize-cli”, and “passport-jwt” .

{"name": "bestpractice","version": "0.0.0","private": true,"scripts": {"start": "nodemon ./bin/www"},"dependencies": {"bcrypt": "^5.0.0","cookie-parser": "~1.4.4","debug": "~2.6.9","express": "~4.16.1","http-errors": "~1.6.3","jade": "~1.11.0","jsonwebtoken": "^8.5.1","morgan": "~1.9.1","mysql2": "^2.2.5","passport": "^0.4.1","passport-jwt": "^4.0.0","sequelize": "^6.3.5"},"devDependencies": {"nodemon": "^2.0.6","sequelize-cli": "^6.2.0"}}

STEP 1.

All the packages displayed in step one above should be installed from the command line before running the server. Other dependencies that are not installed now was installed from my earlier post on express generator and data connection.


npm install bcrypt --save
npm i passport jsonwebtoken passport-jwt --save
npm install passport

STEP 2.

The code in step two displays the configuration information for the passport-crypt authentication where the variable labeled as mysecret is a randomly generated hash uniquely for each project. After making the suggested adjustments to our server file we create a js file within our config directory to set up our passport connection. This file should include the type of strategy which passport will follow as well as the existing models to interact with and a module export setup for such function.

const JwtStrategy = require('passport-jwt').Strategy;const ExtractJwt = require('passport-jwt').ExtractJwt;const models  = require('../models');const users = models.User;const jwtOptions ={}jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();jwtOptions.secretOrKey = "mySecret";module.exports = passport => {passport.use(new JwtStrategy(jwtOptions,(jwt_payload,done) =>{console.log(jwt_payload);users.findOne({where:{id:jwt_payload.id}}).then(user =>{console.log(user);console.log(jwt_payload);if(user){return done(null,user);}return done(null,false);}).catch(err =>{console.log(err);});}));};

The JWT authentication strategy is constructed as follows:

  • secretOrKey is a string or buffer containing the secret (symmetric) or encoded public key (asymmetric) for verifying the token's signature.
  • jsonWebTokenOptions: passport-jwt is verifying the token using jsonwebtoken.
  • jwt_payload is an object literal containing the decoded JWT payload.
  • done is a passport error first callback accepting arguments done(error, user, info).
  • fromAuthHeaderAsBearerToken() creates a new extractor that looks for the JWT in the authorization header with the scheme 'bearer'.

STEP 3.

After such setup in step two, we set up passport for when creating an account and using Jsonwebtoken(JWT) strategy user authentication. In this case, we will be creating an account with a firstname, lastname,email and a password field. We also create a set up to get all data and for single users.

const models = require('../models');const bcrypt = require('bcrypt');const jwt = require('jsonwebtoken');const passport = require('passport');const user = require('../models/user');const JwtStartegy = require('passport-jwt').Strategy;async function register(req,res){const saltRounds = 10;const salt = bcrypt.genSaltSync(saltRounds);const hash = bcrypt.hashSync(req.body.password, salt);var data = req.body;data.password = hash;const user = await models.User.create({firstName:data.firstName,lastName:data.lastName,email:data.email,password:data.password});return res.json("Account successfully created");};async function getData(req,res){const users = await models.User.findAll();res.json(users);}async function getSingleUser(req,res){const users = await models.User.findOne({where:{id:req.params.id}});res.json(users)}module.exports= {register,getData,getSingleUser
}

If there are no errors in the creation of such account then we will be sending the information back to json (res.json) such data and catch any errors if there are any.

STEP 4.

Just like we had a set up for when creating an account, we should have one for when users log in to their account where the email is again the email and the password is the password, just like when we created the user account. We will find an account directly linked to such email here. If the account is found and the password is correct then we will render such information and allow a user to use further functionality. If no user is found, or the password is wrong we’ll return the respective errors as seen in the code displayed below.

const models = require('../models');const bcrypt = require('bcrypt');const jwt = require('jsonwebtoken');const passport = require('passport');const user = require('../models/user');const multer = require('multer');const JwtStartegy = require('passport-jwt').Strategy;async function register(req,res){const saltRounds = 10;const salt = bcrypt.genSaltSync(saltRounds);const hash = bcrypt.hashSync(req.body.password, salt);async function  login(req,res){const data = req.body;const email = data.email;const password = data.password;const user = await models.User.findOne({where:{email:email},}//attributes:['firstname','lastname']);if (user){const checkPassword = bcrypt.compareSync(password, user.password);if (!checkPassword) {return res.json('Incorrect passsword')} else {const payload ={id:user.id,}const token = jwt.sign(payload,"mySecret");return res.json({ "token":token,"data":user,"statusCode":200})}} else {return res.json('No account found ')}}module.exports= {login
}

STEP 5.

We incorporate passport into our routes. Here we will examine the controller which routes the required paths for the account model . In such a file, we should require passport and incorporate our isAuthenticated() function where we target a specific id before rendering the desired information and allow users to have such functionality. Such code is displayed below:

var express = require('express');var router = express.Router();var controller = require('../controller/users');var passport = require('passport');const {getData}=require('../controller/users');
router.post('/register',controller.register);
router.get('/',getData);router.get('/user', passport.authenticate("jwt",{session:false}), controller.getSingleUser );router.post('/login', controller.login)module.exports = router;

We test this using postman:

Post: create a user.

Get: Saved users.

All the users who registered can be seen on the database:

Find code in github: https://github.com/tosin199/Passport_Authentication.git

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store