Click Below to subscribe

How to Implement JWT Authentication in Node.js Using Mysql 2022

JSON Web Token (jwt) is an open standard that allows two parties to securely send data as JSON objects.

In this article, we will implement jwt authentication in express from scratch.

1. Let's create a new express project using express generator.

npm i -g express-generator
express node-mysql-jwt --no-view
cd node-mysql-jwt

2. Create a folder config and inside this create a file database.js.

config/database.js

module.exports = { 
    host: process.env.DB_HOST,
    database: process.env.DB_DATABASE,
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD
}

3. Install dotenv npm package.

npm i dotenv

After Installation import dotenv in app.js

require('dotenv').config();

4. Create a .env file in the root and add these environment variables.

DB_HOST=localhost            // your database host
DB_DATABASE=node-jwt        // your database name
DB_USERNAME=root           // your database username 
DB_PASSWORD=              // your database password

5. Install sequelize and mysql2 npm packages.

npm i sequelize mysql2

6. Create a models folder and inside this create a connection.js and user.model.js

models/connection.js

const { Sequelize } = require('sequelize');
const config = require('../config/database'); 

const sequelize = new Sequelize(config.database, config.username, config.password, {
    host: config.host,
    dialect: 'mysql',
    operatorsAliases: 'false',
    logging: false
});  

module.exports = sequelize

models/user.model.js

const { DataTypes } = require('sequelize');
const sequelize = require('./connection');

const User = sequelize.define('User', {
    name: {
        type: DataTypes.STRING 
    },
    email: {
        type: DataTypes.STRING 
    },
    password: {
        type: DataTypes.STRING 
    }
}, {
    createdAt: 'created_at',
    updatedAt: 'updated_at'
}); 
module.exports = User;

Note:- Don't forget to create users table. https://github.com/ultimateakash/node-mysql-jwt/blob/master/node-jwt.sql

7. Install jsonwebtoken npm package.

npm i jsonwebtoken

8. Create a jwt.js inside the config folder.

config/jwt.js

/*
*    ttl - JWT time to live(seconds or time units(https://github.com/vercel/ms))
*    ttl: 3600 // 1 hour
*    ttl: '1h' // 1 hour
*    ttl: '7d' // 7 days
*/
module.exports = { 
    secret: process.env.JWT_SECRET,
    ttl: 3600
}

9. Open .env file and add JWT_SECRET environment variable.

JWT_SECRET=     // your secret key

Note:- You can use any random string for secret key.

10. Create a utils folder inside this create jwt.js

utils/jwt.js

const jwt = require('jsonwebtoken');
const jwtConfig = require('../config/jwt');

exports.verifyToken = (token) => jwt.verify(token, jwtConfig.secret);

exports.createToken = (data) => jwt.sign(data, jwtConfig.secret, { expiresIn: jwtConfig.ttl });

11. For API validation install joi package.

npm i joi

12. Create a validator.js inside the utils folder.

utils/validator.js

const Joi = require('joi');

module.exports = (type) => (req, res, next) => {
    const schema = getSchema(type);
    if (schema) {
        const result = schema.validate(req.body);
        if (result.error) {
            const { details } = result.error;
            const message = details[0].message.replace(/"|'/g, '');
            return res.status(400).json({
                error: message
            });
        }
    }
    next();
}

const getSchema = (type) => {
    switch (type) {
        case 'register': {
            return Joi.object().keys({
                name: Joi.string().required(),
                email: Joi.string().email().required(),
                password: Joi.string().required()
            })
        }
        case 'login': {
            return Joi.object().keys({
                email: Joi.string().email().required(),
                password: Joi.string().required()
            })
        }
        default: {
            return null;
        }
    }
} 

12. jsonwebtoken package doesn't have revoke token option. so we can implement a blacklist of tokens.

Install keyv npm package.

npm i keyv

By default everything is stored in memory, you can optionally also install a storage adapter.(redis)

Ref:- https://www.npmjs.com/package/keyv

13. Create cache.js inside utils folder.

const Keyv = require('keyv'); 
const keyv = new Keyv();

exports.set = (key, value, ttl = 0) => keyv.set(key, value, ttl);

exports.get = (key) => keyv.get(key); 

exports.del = (key) => keyv.delete(key);

14. Create a middleware folder and inside this create auth.guard.js

middleware/auth.guard.js

const jwt = require('../utils/jwt');
const cache = require('../utils/cache');

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

    let token = req.headers.authorization;
    if (token && token.startsWith('Bearer ')) {
        token = token.slice(7, token.length);
    }

    if (token) {
        try {
            token = token.trim();
            /* ---------------------- Check For Blacklisted Tokens ---------------------- */
            const isBlackListed = await cache.get(token);
            if (isBlackListed) {
                return res.status(401).json({ error: 'Unauthorized' });
            }
            
            const decoded = await jwt.verifyToken(token);
            req.user = decoded;
            req.token = token;
            next();

        } catch (error) { 
            return res.status(401).json({ error: 'Unauthorized' });
        }
    } else {
        return res.status(400).json({ error: 'Authorization header is missing.' })
    }
}

15. Install bcrypt package for hashing passwords.

npm i bcrypt

16. Create a folder controllers and inside this create auth.controller.js

controllers/auth.controller.js

const User = require('../models/user.model');
const cache = require('../utils/cache');
const jwtConfig = require('../config/jwt');
const jwt = require('../utils/jwt');
const bcrypt = require('bcrypt');

exports.register = async (req, res) => {
    const isExist = await User.findOne({
        where:{
            email: req.body.email
        }
    })
    if(isExist) {
        return res.status(400).json({ error: 'Email already exists.' });
    }
    const hashedPassword = await bcrypt.hash(req.body.password, 10);

    const user = await User.create({
        name: req.body.name,
        email: req.body.email,
        password: hashedPassword
    });
    return res.json(user);
}

exports.login = async (req, res) => {
    const user = await User.findOne({
        where: {
            email: req.body.email
        }
    });
    if (user) {
        const isMatched = await bcrypt.compare(req.body.password, user.password);
        if (isMatched) {
            const token = await jwt.createToken({ id: user.id });
            return res.json({
                access_token: token,
                token_type: 'Bearer',
                expires_in: jwtConfig.ttl
            });
        }
    }
    return res.status(401).json({ error: 'Unauthorized' });
}

exports.getUser = async (req, res) => {
    const user = await User.findByPk(req.user.id);
    return res.json(user);
}

exports.logout = async (req, res) => { 
    const token = req.token;
    const now = new Date();
    const expire = new Date(req.user.exp);
    const milliseconds = now.getTime() - expire.getTime();
    /* ----------------------------- BlackList Token ---------------------------- */
    await cache.set(token, token, milliseconds);

    return res.json({ message: 'Logged out successfully' });
}

Note:- In the logout function, this token will be automatically removed after the ttl(milliseconds) is completed

In the above code, milliseconds are the remaining life of the token.

17. Open app.js and update route prefix with api.

app.use('/api', indexRouter);  

18. Update routes.

routes/index.js

const express = require('express');
const router = express.Router();

const authController = require('../controllers/auth.controller');
const authGuard = require('../middleware/auth.guard');
const validate = require('../utils/validator'); 

router.post('/register', validate('register'),  authController.register);
router.post('/login',    validate('login'),     authController.login);
router.get('/user',      authGuard,             authController.getUser);
router.get('/logout',    authGuard,             authController.logout);

router.all('*',  (req, res) => res.status(400).json({'error': 'Bad Request.'}))

module.exports = router;

19. Start the project.

npm start

Finally Open Postman or any REST API Client and test these API's

Checkout my full node-mysql-jwt example.

https://github.com/ultimateakash/node-mysql-jwt

If you facing any issues. don't hesitate to comment below. I will be happy to help you.

Thanks.

Leave Your Comment