How to Authenticate With JWT.
Published: 2023-03-09 20:15:54

---
title: How to Authenticate With JWT.
published: true
date: 2022-11-05 14:51:01 UTC
tags: 
canonical_url: https://mukhtar.hashnode.dev/how-to-authenticate-with-jwt
---

# How to Authenticate With JSON Web Token (JWT)

In this article you are going to learn:

What JSON Web Token (JWT) is.

How to create JSON Web Token (JWT).

How to Authenticate and Authorize With JSON Web Token (JWT)

# Introduction

[JSON Web Token](https://en.wikipedia.org/wiki/JSON_Web_Token) is a standard for creating and sharing encrypted data.

Structure of a JWT :

A JWT contains a **Header** , **Payload** and **Signature**

## Header

The first part of a JWT is the Header, It contains the algorithm that will be used to encrypt and token type.

Here is an example of a header:

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

```

And here is an example of the header in JSON format:

```
{
  "alg": "HS256",
  "typ": "JWT"
}

```

## Payload

The second part of JWT is the Payload, It contains the claims which are statements about the user and additional data.

Here is an example of a payload:

```
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

```

And here is an example of the payload in JSON format:

```
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

```

## Signature

The third or last part of JWT is the signature, It is used to verify the owner of the message and if it wasn't changed along the way, To create the signature the **Base64-encoded header** , **Payload** and **Secret** are taken, then signed along with the algorithm specified in the header.

An example of signature:

```
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

```

And here is how the signature is created:

```
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
 your-256-bit-secret

) secret base64 encoded

```

# Prerequisites

Since we'll be building a Backend Service With Node.js and Express, Knowing All these is an advantage, Though I'll be walking you through it if you don't know them.

JavaScript

Express.js

Some skills in version control

I expect you have Node.js, Any Code Editor, API Testing platform like Postman, MongoDB or any other Database Installed.

## Setting up server

Create a file and then name it anything you like I'll name mine `fullauth` and then initialize the folder with `git init` command.

Now type `code .` in your terminal to open the file, this will only work if you are using VsCode.

The next thing we want to do is initialize npm by executing `npm init -y` in our terminal and we should have our `package.json` in our folder.

Now we need to create all folders and files we need, we can easily do that with this command:

```
mkdir config controller middleware model routes 

touch config/connect.js controller/authcontroller.js middleware/auth.js model/user.js routes/authroute.js .gitignore app.js index.js .env

```

And our folder should look like this :

![fullauth.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1666729045552/FkPSOGuLn.jpg)

## Installing dependencies

And now we'll be installing all dependencies we need with this command:

```
npm i mongoose dotenv express jsonwebtoken bcryptjs nodemon

```

Now we'll add `"dev": "nodemon index.js"` to scripts in `package.json` file and `"type": "module"`, the former is for the server to restart automatically and latter is to enable ES6 modules, our `package.json` file should look like this:

```
{
  "name": "fullauth",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^6.7.0",
    "nodemon": "^2.0.20"
  },
  "type": "module"
}

```

# Getting Started

## Creating a Node.js Server

### Connecting to local MongoDB

We'll add this to `connect.js` file:

```
import mongoose from "mongoose";
import "dotenv/config";

const connect = {
    connectToLocalMongoDB : () =>{
        mongoose
          .connect(process.env.MONGO_URI_LOCAL, {
            useNewUrlParser: true,
            useUnifiedTopology: true,
          })
          .then(() => console.log("DB Connected!"))
          .catch((err) => {
            console.log("not able to connect to database"+ err);
          });
    }
}

export default connect;

```

### Adding `MONGO_URI_LOCAL` to `.env` file

```
MONGO_URI_LOCAL=mongodb://127.0.0.1:27017/fullauth

```

## Updating `app.js` file:

```
import express from 'express'
import connect from "./config/connect.js";
const app = express()

app.use(
    express.urlencoded({
      extended: false,
    })
  );

app.use(express.json());

connect.connectToLocalMongoDB()

export default app

```

## Starting server with `index.js` file

We'll add this to our `index.js` file:

```
import "dotenv/config";
import app from "./app.js";

const PORT = process.env.PORT || 8080

app.get('/', (req, res) =>{
    res.send("Welcome to Full authentication app")
})

app.listen(PORT, () =>{
    console.log(`app is listening on port ${PORT}`)
})

```

### Adding `PORT` to `.env` file

```
PORT=8080

```

Now you can run `npm run dev` to start the server.

## Creating a User model in `user.js` file:

We'll be creating a user model in the `user.js` file, this is what we'll be calling to perform operations on the database:

```

import mongoose from "mongoose";

const User = new mongoose.Schema({
    first_name: {type: String, default: null},
    last_name: {type: String, default: null},
    email: {type: String, default: null},
    password: {type: String, default: null},
})

export default mongoose.model("user", User);

```

## Implementing SignUp and SignIn Controllers

We want to implement the signUp controller, the `/signup` endpoint will be connected to this controller so a new user can register.

What we'll be doing:

- check if user exists
- check if any field is empty
- create a new user and JWT

### Importing dependencies in `authcontroller.js` file :

```
import "dotenv/config";
import user from "../model/user.js";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";

```

### Creating SignUp controller in `authcontroller.js` file:

```
export const signUp = async (req, res) =>{
    const { firstname, lastname, email, password } = req.body

    const oldUser = await user.findOne({email: email})

    if(oldUser){
        return res.status(424).json({
            status: false,
            message: "user already exists"
        })
    }

    if(!(firstname && lastname && email && password)){
        return res.status(424).json({
            status: false,
            message: "all fields are required"
        })
    }

    if( oldUser == null ){
        const encrptedPassword = await bcrypt.hash(password, 12);

        const newUser = await user.create({
            first_name: firstname,
            last_name: lastname,
            email: email,
            password: encrptedPassword,
        })

        const token = jwt.sign({id: newUser._id, }, process.env.JWT_SECRET_KEY,{expiresIn: "2d"});

        return res.status(200).json({
            status: true,
            message: "Registration successful",
            data: {
                token: token
            }
        })
    }
}

```

What we did :

1. We imported all dependencies we'll be using

2. We Checked if user exists 

3. We Created a new user if they do not exist.

4. We used Jwt.sign to create a new token, the `.sign` method takes parameters which includes **Payload** , this contains the claims which are statements about the user and additional data, the second parameter is the secret key we'll use to create a unique hash and then the last parameter I added is the sign option setting the expiry time to 2 days.

### Creating SignIn controller in `authcontroller.js` file:

```
export const signIn = async (req, res) =>{
    try {
        const { email, password } = req.body;

        const oldUser = await user.findOne({email: email})

        if(!(email && password)){
            return res.status(424).json({
                status: false,
                message: "all fields are required"
            })
        }

        if( oldUser == null ){
            return res.status(424).json({
                status: false,
                message: "User does not exist"
            })
        }

        if( oldUser && (await bcrypt.compare(password, oldUser.password))){
            const token = jwt.sign({id: oldUser._id, email:email }, process.env.JWT_SECRET_KEY,{expiresIn: "2d"});
            return res.status(200).json({
                status: true,
                message: "Login Successful",
                data:{
                    token: token
                }
            })
        }else{
            return res.status(424).json({
                status: false,
                message: "Incorrect email or password"
            })  
        } 
    } catch (error) {
        return res.status(500).json({
            status: false,
            message: "An error occurred"
        })  
    }
}

```

What we did :

1. Check if User exist using email address provided

2. If user exists and password matches the one in the Database, create a JWT with Id and email as payload , the secret in our `.env` file as signature and setting the expiry time to 2 days.

3. Return response that includes the JWT.

### Creating a middleware that checks and decode JWT in `auth.js` file :

```
import jwt from "jsonwebtoken";

export const verifyToken = async (req, res, next) =>{
    try {
        const token = req.body.token || req.query.token || req.headers['access-token']

        if(!token){
            return res.status(403).json({
                status: false,
                message: `A token is required for authentication`
            })
        }

        const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY)

        req.user = decoded

    } catch (error) {
        return res.status(401).json({
            status: false,
            message: `Invalid Token`
        })
    }
    return next();
}

```

What we did :

1. Check for JWT in request Body, Query or Header with ' access-token ' and key.

2. If JWT is provided decode it with `jwt.verify` method, this method takes two parameters i.e. the JWT and the secret key that was used to sign it.

3. Assign the decoded object to `req.user` so after authentication routes can assess it.

4. Return " Invalid code " if code is expired or tampered with.

### Creating a controller that greets an authenticated user in `authcontroller.js` file :

```
export const greetUser = async (req, res) =>{
    try {
        const id = req.user.id;

        const oldUser = await user.findById(id);

        if( oldUser ){
            return res.status(200).json({
                status: true,
                message: `Hello ${oldUser.first_name}`
            })
        }else{
            return res.status(498).json({
                status: false,
                message: `Invalid Id`
            })
        }

    } catch (error) {
        return res.status(500).json({
            status: false,
            message: "An error occurred"
        })  
    }
}

```

What we did :

1. Get the id from `req.user` when user is authenticated

2. Find user by id from Database

3. Return response greeting user with name in Database.

## Creating and using Routes

The Next thing we want to do is create endpoints, connect them to controllers in `authroute.js` file and import them in `app.js` file so it can be accessible.

### Creating and Connecting routes in `authroute.js` file :

```
import { Router } from "express";
import { signUp, signIn, greetUser} from "../controller/authcontroller.js";
import { verifyToken } from "../middleware/auth.js";
const router = Router();

router.route("/signup").post(signUp);
router.route("/signIn").post(signIn);
router.route("/greet-user").get(verifyToken, greetUser)

export default router;

```

### Import and use routes in `app.js` file :

```
import authroute from "./routes/authroute.js"

app.use(authroute)

```

Your `app.js` file should look like this :

```
import express from 'express'
import connect from "./config/connect.js";
import authroute from "./routes/authroute.js"
const app = express()

app.use(
    express.urlencoded({
      extended: false,
    })
  );

app.use(express.json());

app.use(authroute)

connect.connectToLocalMongoDB()

export default app

```

## Testing Endpoints in Postman

### Testing Signup Enpoint

Request :

![signup req.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1667652861301/Ph1C6k90A.jpg)

Response :

![signup res.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1667652886169/JzJ44-wbj.jpg)

### Testing Signin Enpoint

Request :

![signin req.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1667653085293/pHO8RrgQV.jpg)

Response :

![signin res.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1667661452372/QdhH90aY1.jpg)

### Testing greet-user enpoint

Inserting JWT in Header :

![Insert header.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1667653270863/z_lzHMAAf.jpg)

Request :

![greet req.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1667653388947/pg1Qanh7I.jpg)

Response :

![greet res.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1667653410273/Tfp2E2oif.jpg)

# Conclusion

So we've come this far and I guess you should have learnt how to

- Create a JWT
- Use JWT in signup and signin enpoints
- Extract JWT from header and Decode it
- How to use object from JWT when user passes authentication

> Perfection is achieved not when there is nothing more to add, but rather when there is nothing more to take away. Antoine de Saint-Exupery

You can learn more about JWT **[Here](https://jwt.io/)**

Thanks for Reading.

You can get the complete code **[Here](https://github.com/DevMukhtarr/fullauth)**

Follow me for more backend development tips.