Admin Routes And Auth Middleware express JWT in Node.js

node.js

Let’s protect our routes and create roles for our users. And make admin routes and middlewares to check if the user logged in or have the authorization to visit a route.

First of all, you need to see part 1 of this course to understand this part

Authentication In Node.js And MongoDB.

Adding roles to the user model

to differentiate between the end user and the admin. We need to add roles. Some developers like to make a separate model for the admin but I like to make all the users in one model and add roles to each user and protect my routes by middleware.

Open the models\user.js file and add the role property with enum values for validation as the following.

role : { type: String, enum: {
    values: ['user', 'admin', 'super-admin'],
    message: '{VALUE} is not supported'
  }},

We add three types of users an end user, an admin, and a super admin.

this is how the user model should look like.

const mongoose = require("mongoose");
let uniqueValidator = require('mongoose-unique-validator');

const userSchema = new mongoose.Schema
({
  first_name: { type: String, required: true},
  last_name: { type: String, required: true},
  email: { type: String, unique: true, required: true},
  role : { type: String, enum: {
    values: ['user', 'admin', 'super-admin'],
    message: '{VALUE} is not supported'
  }},
  password: { type: String, required: true},
  token: { type: String },
});

userSchema.plugin(uniqueValidator);

module.exports = mongoose.model("user", userSchema);

In the controllers\authController.js file, Let’s assign every newly registered user a user role by default.

  exports.register = async (req, res, next) => {
//
//
  const user = await User.create({
        first_name,
        last_name,
        email: email.toLowerCase(), 
        password: encryptedPassword,
        role:'user' // assign  a user role by default
      });

//
//
}

That’s for the end user but for admin, we have a lot to do, for now, we will create the super admin only.

Let’s create a new seeder for creating admin for us. create a new folder in the root folder and name it seeder and make the admin seeder file inside it seeders\admin.js

const User = require('../models/user')
const bcrypt = require("bcrypt");
exports.superAdmin = async ()=>{
    try{
    let password = await  bcrypt.hash('admin@1234', 10);
    let admin = await User.findOne({email:'admin@admin.com'})
    if(!admin){
    User.create({
        first_name:'super',
        last_name:'admin',
        email:'admin@admin.com',
        password: password,
        role:'super-admin'
    })}
   }catch(error){
    console.log(error)
   }
}
  • This module will create the super admin when we run the app
  • First, it checks if the admin email is not used then it creates the admin with the credentials we give to it.

Let’s add the admin seeder to the app.js file and run the app to create the super admin for us.

//
var app = express();
//
//run seeders
const {superAdmin} = require('./seeders/admin');
superAdmin();
//
//

Run the app the check the logs for any errors, it should show no errors then try to log in with the super admin credentials.

http://localhost:3000/auth/login

{
   "email": "admin@admin.com",
   "password":"admin@1234"
}

Auth Middleware express JWT

Let’s protect our routes, we will create two middleware the first one for authenticated users and the second one for the admin users.

Express Auth Middleware

Create and new file middlewares\auth\auth.js and folders if not exist. this middleware is for checking if the users exist in our database and if the user logged in by the JWT access token, the users get when they log in.

var jwt = require('jsonwebtoken');
const { PRIVATE_KEY } = process.env;
const User = require("../../models/user");
module.exports = async (req, res, next) => {
  try {
    const token = req.headers.authorization.split(" ")[1];
    let user = jwt.verify(token, PRIVATE_KEY);
    let userX = await User.findById(user._id).select('-password')
    req.user = userX;
    next();
  } catch (error) {
    console.log("auth error", error);
    res.status(401).json({
      message: "authentication failed",
      error: error
    });
  }
};

Code explanation:

  • We get the token that was sent with the request and assign it to const token
  • By that token we get the user data by jwt but it only gets the _id and email
  • So that we get all the user data via findById except for the password and passing the user data to the request data to be used in the other functions of the pipeline.

Express Admin Middleware

This middleware will check if the user is an admin or super admin to access a route. the admin middleware must be assigned to a route after the auth route.

Let’s create the middleware middlewares\auth\admin.js and add the following code.


module.exports = async (req, res, next) => {
  try {
  
    if (req.user.role == 'admin' || req.user.role == 'super-admin') {
      next();
    } else {
      res.status(401).json({
        message: "not allowed" 
      });
    }
  } catch (error) {
    console.log(error);
    res.status(401).json({  
      message: "admin authentication failed",
    });
  }
};
  • Auth middleware sends the user data to the admin middleware and we check the user role if (req.user.role == 'admin' || req.user.role == 'super-admin')
  • If true the go to the next function in the pipeline if false it returns an error with the message “not allowed”

Assign the middlewares to a route for testing

We will create routes and controller for categories that we will use later in this course but now we just test on them.

Create a controller controllers\admin\categoryController.js and add the following code.


exports.index = async (req, res, next) => {

}

exports.create = async (req, res, next) => {

}

exports.store = async (req, res, next) => {

}

exports.edit = async (req, res, next) => {

}

exports.update = async (req, res, next) => {

}

exports.show = async (req, res, next) => {

}

exports.destroy = async (req, res, next) => {

}

Then create these routes in routes\admin\category.js

var express = require('express');
var router = express.Router();
//calling the middlewares
var auth =  require("../../middlewares/auth/auth");
var admin =  require("../../middlewares/auth/admin");
//calling the functions from the controller
const { create, store, edit, update, destroy, index, show} = require("../../controllers/admin/categoryController");

//create
router.get('/create',auth,admin, create);
router.post('/store',auth,admin, store );
//edit
router.get('/:id/edit',auth,admin, edit );
router.put('/update/:id',auth,admin, update );
//show,index
router.get('/:id',auth,admin,show );
router.get('/',auth,admin,index );
//delete
router.delete('/:id',auth,admin,destroy);

 module.exports = router;

As you can see we added auth middleware before the admin and at the end, we added the controller function. router.get('/create',auth,admin, create);

let’s add the route to app.js

//
var adminCategoryRouter = require('./routes/admin/category');
//
app.use('/admin/category', adminCategoryRouter);

We can test the API on postman http://localhost:3000/admin/category/create

and select the Authorization type to be Bearer Token and ad jwt token for the login and test it once with a normal user and once with the admin.