DEV Community

Cover image for Authentication and Authorization Using JWT in Node JS with Express
muhammed Lotfy
muhammed Lotfy

Posted on • Edited on

Authentication and Authorization Using JWT in Node JS with Express

Table Of Contents

Introduction

User authentication & authorization is one of the important part of any web application.
There are several kinds of way to handle authentication, we can rely on third party service like Passport.
But in this article we will use very simple & self developed approach, which will help us to understand core part of authentication.

Technology Used

Token: JWT
Password Hash: bcryptjs
Database: MongoDB

What is authentication & authorization ?!

In simple words, Authentication is the process of verifying who a user is (who you are), and
Authorization is the process of verifying what they have access to (what you are allowed to do).

haa

What is (JWT)JSON Web Token ?!

JWTs are a good way of securely transmitting information between parties because they can be signed (Information Exchange). Even though we can use JWT with any type of communication method, today JWT is very popular for handling authentication and authorization via HTTP.

Here is the diagram of how JWT works:

Alt Text

Structure of a JWT :-

  1. The first section of the JWT is the header, which is a Base64-encoded string.
    which contains the hashing algorithm, which was used to generate the sign and the type of the token.
    If you decoded the header it would look something similar to this:
    { "alg": "HS256", "typ": "JWT"}

  2. The second section is the payload that contains the JSON object of user data like (id or id_type) that was sent back to the user.
    Since this is only Base64-encoded, it can easily be decoded by anyone.

{"sub": "1234567890", "name": "John Doe", "userId": "1516239022", "user_type_id":1 }

  1. The final section is the signature of the token using the algorithm that is mentioned in the header section

Structure of a JWT

Why using Authentication & Authorization ?!

To protect the data in a security system, administrators should be able to, among other things, implement detailed user access privileges, select the information that can be shared internally with partners and authorities, and control how long data is kept.

A common example is entering a username and password when you log in to a website.
Entering the correct login information lets the website know

1) Who you are and
2) That it is actually you accessing the website.

Let's Start

There will be two types of users (admin & user)

Authentication that all user need to login and register first ,
Authorization that administrators and the regular user. Administrators will be able to view special events
whereas the regular user will be able to view ordinary events

To get started in this trip, I have REST APIs already created (including 4 Routes) GitHub repo

1- POST login route ( everyone has access)
2- POST register route ( everyone has access)
3- GET events array (only regular user has access)
4- GET special events array (only admin user has access)

How apply Authentication ?!

Step (1)

  1. To get started, in your terminal initialize an empty Node.js project with default settings:
    $ npm init -y

  2. Then, let's install the Express framework, JWT, bcryptjs and mongoose:
    $ npm install --save express jsonwebtoken bcryptjs

  3. Then To create basic structure for different kind of endpoints like registration or login we will use express as router. And we will create a folder for routers (routers/index.js)

  4. Then, let's create a file called (middleware/auth.js), which will be our authentication service and create a file called (controllers/user), which will be our controller for user functions

  5. Now, let's create our Server and use these modules and configure them in the Express app (server.js):

const express = require('express');
const app = express();

app.use(express.json()); 

// Import Routes
const authRoute = require('./routes/index');

// Route Middlewares
app.use('/api', authRoute);

const port = 3000;
app.listen(port, function(){console.log("Server running on localhost:" + port);});
Enter fullscreen mode Exit fullscreen mode

Step (2)

  1. Now go for folder for routers (routers/index.js) to configure express Router import userController
const router = require('express').Router();
const userController = require('../controllers/user');

// Register a new User
router.post('/register', userController.register);

// Login
router.post('/login', userController.login);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Step (3)

Now go for folder for routers (controllers/user) to add userController functions
1.Connect to DB

mongoose.connect(db, function(err){
    if(err){
        console.error('Error! ' + err)
    } else {
      console.log('Connected to mongodb')      
    }
});
Enter fullscreen mode Exit fullscreen mode
  1. Create register function
    • Hash password using bcrypt module
    • Create an user object
    • Save User in the database
    • Create payload then Generate an access token (if you ask about what is payload go for Structure of a JWT section)
exports.register = async (req, res) => {
    //Hash password
    const salt = await bcrypt.genSalt(10);
    const hasPassword = await bcrypt.hash(req.body.password, salt);

    // Create an user object
    let user = new User({
        email: req.body.email,
        name: req.body.name,
        password: hasPassword,
        user_type_id: req.body.user_type_id
    })

    // Save User in the database
    user.save((err, registeredUser) => {
        if (err) {
            console.log(err)
        } else {
            // create payload then Generate an access token
            let payload = { id: registeredUser._id, user_type_id: req.body.user_type_id || 0 };
            const token = jwt.sign(payload, config.TOKEN_SECRET);
            res.status(200).send({ token })
        }
    })
}
Enter fullscreen mode Exit fullscreen mode

After the authentication service is up and running, let's send a POST request and see if registration works or not.

  • I will be using the rest-client Postman to do this. Feel free to use any rest-client you prefer or something like Insomnia to do this.
    • Let's send a post request to the http://localhost:3000/api/register endpoint with the following JSON: { "email":"d@a.hh", "name" : "lotfy", "password": "123456", "user_type_id":1 }
    • You should get the access token as the response: { "Token": "eyJhbGciOiJIUz..." }

Alt Text

Step (4)

Now we register for new user and token receive token as response
we need to login with user credentials

  1. create login function
    • compare Hashed password with credentials
    • create payload then Generate an access token and return token in headers
exports.login = async (req, res) => {

    User.findOne({ email: req.body.email }, async (err, user) => {
        if (err) {
            console.log(err)
        } else {
            if (user) {
                const validPass = await bcrypt.compare(req.body.password, user.password);
                if (!validPass) return res.status(401).send("Mobile/Email or Password is wrong");

                // Create and assign token
                let payload = { id: user._id, user_type_id: user.user_type_id };
                const token = jwt.sign(payload, config.TOKEN_SECRET);

                res.status(200).header("auth-token", token).send({ "token": token });
            }
            else {
                res.status(401).send('Invalid mobile')
            }

        }
    })
}
Enter fullscreen mode Exit fullscreen mode

let's send a POST request and see if Login works or not.

  • Let's send a post request to the http://localhost:3000/api/login endpoint with the following JSON: { "email":"d@a.hh", "password": "123456" }
  • You should get the sucess with 200 code and access token in the header of response: "auth-token": "eyJhbGciOiJIUz..." Login

How apply Authorization ?!

As we mentioned in Authentication steps (register/login)
We added property called "user_type_id" which is identifier for type of user is request this data

Step (1)

Now we need if the request for logged in user or some hack my APIs
let's move to our auth file in (middleware/auth.js)

  • Check if request have an access token
  • Remove Bearer from string which added in header of request
  • Verify User Token with my token secretKey
exports.verifyUserToken = (req, res, next) => {
    let token = req.headers.authorization;
    if (!token) return res.status(401).send("Access Denied / Unauthorized request");

    try {
        token = token.split(' ')[1] // Remove Bearer from string

        if (token === 'null' || !token) return res.status(401).send('Unauthorized request');

        let verifiedUser = jwt.verify(token, config.TOKEN_SECRET);   // config.TOKEN_SECRET => 'secretKey'
        if (!verifiedUser) return res.status(401).send('Unauthorized request')

        req.user = verifiedUser; // user_id & user_type_id
        next();

    } catch (error) {
        res.status(400).send("Invalid Token");
    }

}
Enter fullscreen mode Exit fullscreen mode
  • Now we need know type for logged-in user
exports.IsUser = async (req, res, next) => {
    if (req.user.user_type_id === 0) {
        next();
    }
    return res.status(401).send("Unauthorized!");   
}
exports.IsAdmin = async (req, res, next) => {
    if (req.user.user_type_id === 1) {
        next();
    }
    return res.status(401).send("Unauthorized!");

}
Enter fullscreen mode Exit fullscreen mode

Step (2)

Now we verified user but need to know is this route for administrators or regular user
with my authentication middleware to check routes with user type to file (routers/index.js)

  • If these valid conditions call get user events function
// Auth user only
router.get('/events', verifyUserToken, IsUser, userController.userEvent);

// Auth Admin only
router.get('/special', verifyUserToken, IsAdmin, userController.adminEvent);
Enter fullscreen mode Exit fullscreen mode

let's send a request and see if special route works for admin user with user_type_id == 1.

  • Let's send a post request to the http://localhost:3000/api/special endpoint with the following authorization header with TYPE Bearer Token and value of token:eyJhbGciOiJIUz...

  • You should get the success with 200 code and the special events array as a response:
    { "_id": "1", "name": "Auto Expo Special",
    "description": "lorem ipsum", "date": "2012-04-23T18:25:43.511Z" } .....

    special Array

Conclusion ?!

In this article, we have introduced you to JWT and how to implement JWT with Express. I hope that now you have a piece of good knowledge about how JWT works

The end Thank you for going through this tutorial. If you notice any errors please report them to me. If you got stuck on any step, please refer to this GitHub repo.

You can contact me through:

Gmail: mhmdlotfy9@gmail.com
or Linkedin

Top comments (6)

Collapse
 
abdalla_wasfy profile image
Abdallah Wasfy

Good Job 👌

Collapse
 
mohammedgmgn profile image
Mohammed Abdullah

Perfect 👏👌

Collapse
 
mhmdlotfy96 profile image
muhammed Lotfy

thanks 😍

Collapse
 
jobayerdev profile image
Jobayer Hossain • Edited

Perfect, Thank You

Collapse
 
yrgamit profile image
Yrga well

I realy like it ,Thank you very much.

Collapse
 
yrgamit profile image
Yrga well

Thank you!