Authentication in Node.js

Authentication plays an important role in the security of the application. If you don’t have the right authentication and authorization when you share your resources via routes to your users your application’s security is compromised. You can give your users access based on their rights and user type. The usernames and encrypted passwords are stored in a password file on the server side. This password and username are asked when a user tries to access the resource and if its valid user is taken to the page he/she requested. Let’s create a folder nodejs-authentication and set up the app template.


mkdir nodejs-authentication

cd nodejs-authentication

npm init --yes


For retrieving jwtPrivatekey we use Config, joi is used for validation, express for the web framework, mongoose to create models and schema, jsonwebtoken to generate and verify JWt and bcrypt for hashing password. Install all these dependencies.

npm i config joi express mongoose jsonwebtoken bcrypt

Create the files and folders needed for the application. Add the following lines of code in 


default.json.

{

 "myprivatekey": "myprivatekey"

}


Set an environmental variable myprivatekey to use in our application.


const config = require('config');

const jwt = require('jsonwebtoken');

const Joi = require('joi');

const mongoose = require('mongoose');


//simple schema

const UserSchema = new mongoose.Schema({

  name: {

    type: String,

    required: true,

    minlength: 3,

    maxlength: 50

  },

  email: {

    type: String,

    required: true,

    minlength: 5,

    maxlength: 255,

    unique: true

  },

  password: {

    type: String,

    required: true,

    minlength: 3,

    maxlength: 255

  },

  //give different access rights if admin or not 

  isAdmin: Boolean

});



//custom method to generate authToken 

UserSchema.methods.generateAuthToken = function() { 

  const token = jwt.sign({ _id: this._id, isAdmin: this.isAdmin }, config.get('myprivatekey')); //get the private key from the config file -> environment variable

  return token;

}


const User = mongoose.model('User', UserSchema);


//function to validate user 

function validateUser(user) {

  const schema = {

    name: Joi.string().min(3).max(50).required(),

    email: Joi.string().min(5).max(255).required().email(),

    password: Joi.string().min(3).max(255).required()

  };


  return Joi.validate(user, schema);

}


exports.User = User; 

exports.validate = validateUser;


Our schema has a name, email, and password. To generate an authentication token we have added a method. We have exported validateUser. It will be used to check if all required fields are filled. 


 const jwt = require("jsonwebtoken");

const config = require("config");


module.exports = function(req, res, next) {

  //get the token from the header if present

  const token = req.headers["x-access-token"] || req.headers["authorization"];

  //if no token found, return response (without going to the next middleware)

  if (!token) return res.status(401).send("Access denied. No token provided.");


  try {

    //if can verify the token, set req.user and pass to next middleware

    const decoded = jwt.verify(token, config.get("myprivatekey"));

    req.user = decoded;

    next();

  } catch (ex) {

    //if invalid token

    res.status(400).send("Invalid token.");

  }

};


This is the custom middleware we will use to check if there is valid JWT in request headers. 

In our application we have two routes defined. One to get the current user, second one to register the user. To hash the password we will use bcrypt. 


Index.js


const config = require("config");

const mongoose = require("mongoose");

const usersRoute = require("./routes/user.route");

const express = require("express");

const app = express();



//use config module to get the privatekey, if no private key set, end the application

if (!config.get("myprivatekey")) {

  console.error("FATAL ERROR: myprivatekey is not defined.");

  process.exit(1);

}


//connect to mongodb

mongoose

  .connect("mongodb://localhost/nodejsauth", { useNewUrlParser: true })

  .then(() => console.log("Connected to MongoDB..."))

  .catch(err => console.error("Could not connect to MongoDB..."));



app.use(express.json());

//use users route for api/users

app.use("/api/users", usersRoute);


const port = process.env.PORT || 3000;

app.listen(port, () => console.log(`Listening on port ${port}...`));


Run the index.js file. We will get an error first that is because we haven’t set the environment variable to sign our tokens.


$env:myprivatekey = "myprivatekey"

node index.js


Now you can run the app. It will print the following.


Listening on port 3000...

Connected to MongoDB…