How to Secure Rest Api Using Jwts?

  1. GitHub Repository Link

» What are JWTs?

  1. JWTs are a security standard that provides a method for securely transferring data between the client and a backend system.

  2. JWTs are used for authentication and authorisation of the users.

  3. Also JWTs consist of three parts and are seperated by a dot(.). A header, payload and a signature.

JWTs Image

👉🏻 Image from JWT.io

  1. So basically a header contains information about how JWT is encoded.

  2. And a payload, contains the actual data being transmitted.

  3. And finally a signature is created by combining the encoded header, encoded payload and a secret key.

» What we are building?

  1. In this blog, we will look at how we can secure our APIs using the JSON web tokens. And believe me, it’s simple. So let’s get into it.

» Initialize the project.

  1. Open the terminal and create a new folder and name it anything you want and then in that folder run the command:
npm init -y 
  1. And then run the below command into your project directory to install the dependencies we need.
npm install cors dotenv bycrpt mongoose cookie-parser crypto jsonwebtoken mongodb
  1. Project Structure for your convenience Project Structure

» Initializing the Database

  1. Head over to MongoDB Website.

  2. Sign in and follow the steps:

  3. Create a project : Create a project

  4. And name it Name a Project

  5. Choose a plan

Name a Project

  1. Create a deployment Name a Project

  2. Get the connection String

Name a Project

  1. Go back to VS code and in here create a .env file. Inside this file create a variable named CONNECTION_STRING and then paste the connection string we got from mongodb website.
// .env file
CONNECTION_STRING=mongodb+srv://username:[email protected]/

» Make connection to DB

  1. Next, we want to make connection to our database using mongoose. So let’s create a utils folder and inside it create a file db.js and inside this file we start import mongoose using the require function. Then we declare a connectDB function is an asynchronous function that will help us connect to a MongoDB database.
const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.CONNECTION_STRING);
    console.log("Connected to MongoDB!");
  } catch (error) {
    console.error("Error connecting to MongoDB:", error);
  }
};

module.exports = connectDB;
  1. If the connection is successful, a message “Connected to MongoDB!” is logged to the console. If an error occurs during the connection process, the catch block will be executed. The error will be logged to the console with the message “Error connecting to MongoDB:”.

  2. Now connect the mongoDB database in VS code with connection string that we copied earlier. Open the command palette and type ‘Mongo Connection’ and then paste the string and hit enter. You should get a message ‘MongoDB Connection Successfull.’

Name a Project

  1. Next we will create a define a mongoose model for a user. A schema defines the structure of the documents within a collection in MongoDB. In our case, the schema specifies that each User document should have a username and a password. The username is a simple string, while the password is an object with additional properties.
// /models/user-models.js
const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.CONNECTION_STRING);
    console.log("Connected to MongoDB!");
  } catch (error) {
    console.error("Error connecting to MongoDB:", error);
  }
};

module.exports = connectDB;

» Create Controllers.

  1. Now we will create controllers for our endpoints. For that create a controllers folder and inside it create a userControllers.js file.

  2. This JavaScript file, userControllers.js, defines three controller functions for handling user-related requests: registerUser, loginUser, and getUsers. And getUsers is a protected route

// /controllers/userControllers.js
const User = require("../models/user.model");
const bcrypt = require("bcrypt");
const { generateToken } = require("../middleware/auth");

exports.registerUser = async (req, res) => {
  const { username, password } = req.body;

  try {
    const hash = await bcrypt.hash(password, 10);
    await User.create({ username, password: hash });
    res.status(201).send({ message: "User registered successfully" });
  } catch (error) {
    console.log(error);
    res.status(500).send({ message: "An error occurred!! " });
  }
};

exports.loginUser = async (req, res) => {
  const { username, password } = req.body;

  try {
    const user = await User.findOne({ username });

    if (!user) {
      return res.status(404).send({ message: "User not found" });
    }

    const passwordMatch = await bcrypt.compare(password, user.password);

    if (!passwordMatch) {
      return res.status(401).send({ message: "Invalid login credentials" });
    }

    const payload = { userId: user._id };
    const token = generateToken(payload);
    res.cookie("token", token, { httpOnly: true });
    res.status(200).json({ message: "Login successful" });
  } catch (error) {
    console.log(error);
    res.status(500).send({ message: "An error occurred while logging in" });
  }
};

exports.getUsers = async (req, res) => {
  try {
    const users = await User.find({});
    res.json(users);
  } catch (error) {
    console.log(error);
    res.status(500).send({ message: "An error occurred!!" });
  }
};

» Create Middlewares

  1. Create two files in middleware folders: auth.js and config.js

  2. In auth.js file: auth.js, is responsible for generating and verifying JSON Web Tokens (JWTs) for user authentication.

// /middleware/auth.js
const jwt = require('jsonwebtoken');
const { secretKey } = require('./config');

const generateToken = (payload) => {
  const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
  return  token ;
};

const verifyToken = (req, res, next) => {
  const token = req.cookies.token;

  if (!token) {
    return res.status(401).json({ message: 'No token provided' });
  }

  jwt.verify(token, secretKey, (err, decoded) => {
    if (err) {
      return res.status(401).json({ message: 'Invalid token' });
    }

    req.userId = decoded.userId;
    next();
  }); 
};

module.exports = { generateToken, verifyToken };
  1. In config.js file: This file generates a secret key.
const crypto = require('crypto');

module.exports = {
  secretKey: crypto.randomBytes(32).toString('hex')
};

» Define API endpoints

  1. Now we will define our API endpoints. Let’s create a routes folder and inside it create a userRoutes.js file.
const express = require('express');
const router = express.Router();
const userControllers = require('../controllers/userControllers');
const { verifyToken } = require('../middleware/auth');
router.post('/api/register', userControllers.registerUser);
router.post('/api/login', userControllers.loginUser);
router.get('/api/users', verifyToken, userControllers.getUsers);
module.exports = router;
  1. This JavaScript file, userRoutes.js, is setting up the routes for user-related operations in an Express.js application.

» Create entrypoint for our express application

  1. Now we will create an entry point for our application. So create a file name server.js in the root directory.

  2. This JavaScript file, server.js, is the main entry point for an Express.js application.

const express = require('express');
const cors = require('cors');
const app = express();
const port = 5000;
require('dotenv').config();
const connectDB = require('./utils/db');
const cookieParser = require('cookie-parser');

connectDB();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(cookieParser());
const userRoutes = require('./routes/userRoutes');
app.use('/', userRoutes);

app.listen(port, () => {
  console.log(`Server is listening at http://localhost:${port}`);
});
  1. We will then start the server, so for that first update the package.json file and add a start script.
"start": "node server.js"

» Test on Postman

  1. Now we will test these endpoints on postman. So open the postman app and make a post request

And That’s all for this blog. I hope you found it usefull. So I’ll see you in next one. Till then bye bye and take care. ✨