UDERSTANDING JWT -JSONWEBTOKEN
Tags: webdev, programming, tutorial
Published: 2021-12-23 22:10:48


In **token-based authentication**, we store the user’s state on the client. **JSON Web Token (JWT)** is an open standard (**RFC 7519**) that defines a way of securely transmitting information between a client and a server as a JSON object. In this blog, I will use tokens and JWT terms to demonstrate how the transmission between the client and the server actually works. 

### ANATOMY OF JWT

The anatomy of a JWT  contains three forms that is the  Header , Payload and the Signature . The headers tells the signing algorithm of the token, payload is the data that  the JWT contains which can be the users profile and other information and the signature part verifies the authenticity of the parts. The pieces of data are referred to us claims. 


![1_u3a-5xZDeudKrFGcxHzLew.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1639998899353/srvh4A8Kl.png)

#### Example of JWT

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```


The above example uses the  Base64 URL-encoded JSON object. It contains information describing the type of token and the signing algorithm used, such as HMAC, SHA256, or RSA.

#### Example 

```
{
   "alg": “RSA”,
   "typ": "JWT"
}

```

The JWT **Payload** contains claims that are pieces of information asserted about a subject. The claims will contain the statements about the user and any other additional information. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature. Claims will either be registered, public or private.

#### Example 

```
{
   “Username”: “Jackman”,
   “Email”: "Jackman@example.com”,
   "admin": true
}
```
 Creating the JWT **signature** involves taking the encoded header, payload,  the signing using the signing algorithm specified  and a secret key.

#### Example

```
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

```

The above will create a JWT token using the **HMAC SHA256 algorithm**. This is the signature to verify that the token has not been modified  when the secret key is applied on the back-end. Meaning anyone who have the token can see the data of the token unless encrypted  but they cannot modify the data in the token unless they have the secrete key which should not be possible  since the server is the only thing that have access to the secrete key.

### HOW JWT IS USED 

-  The user logs in ( they send their credentials to the server and the server will check if it's correct and logs in)
- Upon the request, the server verifies the credentials and generates  a JWT token containing the user information and sends it back to the client (front-end)
- The front-end then stores the JWT (usually in cookies, sessions or local storage) and includes this JWT whenever it needs privilege access to.
- The server then uses the JWT to verify the validity of the token that the client sent is legit and has not been modified.

But before the front-end send a JWT to the server ,it will look like this in the request header  **{authentication:'Bearer  <token>’}**


### SIGNING VRS ENCYPTING TOKENS

Signing proves that the data in the JWT has not been modified, where as  encrypting is  like preventing the data in the JWT from being seen by third parties. JWT are not encrypted by default.

### ADVANTAGES OF JWT (Token-based authentication)

-   Token based  authentication is stateless  (all the information is inside the token itself)

-  They also uses JSON(JavaScript object notation) which is compact  and secure compared to others like xml.

- The data is stored in the JWT, meaning it can contain any type of data giving the flexibility on what information is to be included in the token.

-  And Tokens remain valid until they expire or until the data has been modified.

-  Logout is also simple by destroying  the token  in the browser without server interaction.


**NOTE:**The user will have to re-authenticate the tokens when they expire so they have to login every two days or something depending on the expiration date set on the token. 

### LIMITATIONS

- Storing a lot of data in the token makes it huge, which slows the requests.

- Token in the client-side might be hijacked by an attacker making it vulnerable to Cross-site scripting (XSS) attacks which often occurs when an outside entity can execute code within your domain by embedding malicious JavaScript on the page to read and compromise the contents of the browser storage.




### DEMO
To carry on with this Demo, you must have NodeJs, MongoDB installed on your system, you can install the latest MongoDB version [here](https://docs.mongodb.com/manual/installation/), latest NodeJs version [here](https://nodejs.org/en/),you must also know how to use basic terminal commands and have postman account, you can sign up [here](https://www.postman.com/). 

We will  build a simple RESTful API to use JWT to understand how all this works. To begin you need to get the starter template on my GitHub [here](https://github.com/evans646/jwt-demo). Follow the instructions in the **README.md** to get the server up and running and come back to this demo. 

There are a few packages we will need to install :

1.  [Bcrypt](https://www.npmjs.com/package/bcrypt)  to encrypt the user password.
2. [dotenv](https://www.npmjs.com/package/dotenv) to configure the environment variables and,
3. [Jsonwebtokens ](https://www.npmjs.com/package/jsonwebtoken)


So open the project and install the packages we will need , in the project directory (outside the src directory) run this command :

```
npm install  bcrypt  dotenv jsonwebtoken
``` 


After these are installed , we will have to configure the signup route to be able to sign up users and get the user information:


![Screen Shot 2021-12-22 at 10.53.01 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1640174213525/Tz_u3mtaO.png)
**src -> routes/ :**

```
touch signUpRoute.js

```

Good! now let’s import the packages and add the following code to the **signUpRoute.js** file :

```
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { getDbConnection } from '../db';


export const signUpRoute = {
    path: '/api/signup',
    method: 'post',
    handler: async (req, res) => {
        const { email, password } = req.body;
        // this is the name of the database 
       //which  will be created by default 
        const db = getDbConnection('jwt-demo-db');
        // checking to see if the user already exists in the db
        const user = await db.collection('users').findOne({ email });
        if (user) {
            res.sendStatus(409);
        };
      //if no user exist in the database with such credentials, 
      //we hash the password and save the user to the db 
        const passwordHash = await bcrypt.hash(password, 10);
        const personalInfo= {
            hairColor: 'brown',
            favoriteFood: 'rice ',
            bio: 'I am the father of all',
        };
        const result = await db.collection('users').insertOne({
            email,
            passwordHash,
            info: personalInfo,
            isAdmin: false,
        });
        const { insertedId } = result;
 // code here 
    }
};
```
With this in place, we can now generate json web token for the information that will be send back to the client so they can store and use it. We need to configure the **JWT environment variable**, so in the top level folder outside the src folder , run this command  :

```
touch .env 
```

This will create a new file for us  so we can insert out secret key:

```
JWT_SECRET=thywillbedone
```
**NOTE :** you can change the secrete key to your own

Now we can  generate  json web token by adding this code just below the inserted id on the **signUpRoute.js :**

```
  jwt.sign({
            id: insertedId,
            email,
            info: personalInfo,
            isAdmin: false,
        },
        process.env.JWT_SECRET,
        {
            expiresIn: '2d',
        },
        (err, token) => {
            if (err) {
                return res.status(500).send(err);
            }
            res.status(200).json({ token });
        });
```

Making the complete **signUpRoute.js** file look like this :

```
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { getDbConnection } from "../db";

export const signUpRoute = {
  path: "/api/signup",
  method: "post",
  handler: async (req, res) => {
    const { email, password } = req.body;
    // this is the name of the database which  
    // will be created by default
    const db = getDbConnection("jwt-demo-db");
    // checking to see if the user already exists
    const user = await db.collection("users").findOne({ email });
    if (user) {
      res.sendStatus(409);
    }
    //if no user exist in the database with such credentials,
    //we hash the password and save the user to the db
    const passwordHash = await bcrypt.hash(password, 10);
    const personalInfo = {
      hairColor: "brown",
      favoriteFood: "rice ",
      bio: "I am the father of all",
    };
    const result = await db.collection("users").insertOne({
      email,
      passwordHash,
      info: personalInfo,
      isVerified: false,
    });
    const { insertedId } = result;

    jwt.sign(
      {
        id: insertedId,
        email,
        info: personalInfo,
        isAdmin: false,
      },
      process.env.JWT_SECRET,
      {
        expiresIn: "2d",
      },
      (err, token) => {
        if (err) {
          return res.status(500).send(err);
        }
        res.status(200).json({ token });
      }
    );
  },
};
```

Great!!!, but we need to add the route to the index file to be able to make it work :

**src -> routes -> index.js**   and add :

```
import { signUpRoute } from './signUpRoute';

```

Now add **signUpRoute** to the routes array :


```
import { testRoute } from './testRoute';
import { signUpRoute } from './signUpRoute';

export const routes = [
    testRoute,
    signUpRoute,
];
```
>Don't worry too much about the structures of the routes unless you are curious 👀 It is also another efficient way of structuring our app

Now we can start our server and test our route with postman by sending a POST request to :

 http://localhost:8080/api/signup


Firstly we need to make the server pay attention to the environment  variable so let's  add  ( **-r dotenv/config**) by editting  the **package.json** file scripts section to this:
```
  "scripts": {
    "start": "nodemon --exec babel-node -r dotenv/config ./src/server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
``` 
So the **package.json** file should also look like this :
```
{
  "name": "back-end",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon --exec babel-node -r dotenv/config ./src/server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/core": "7.13.10",
    "@babel/node": "7.13.12",
    "@babel/preset-env": "7.13.12",
    "bcrypt": "^5.0.1",
    "dotenv": "^10.0.0",
    "express": "4.17.1",
    "jsonwebtoken": "^8.5.1",
    "mongodb": "3.6.5",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "nodemon": "2.0.7"
  }
}
``` 



Now start the server and set the accept parameters  in postman to be:  raw - json

![Screen Shot 2021-12-22 at 11.11.46 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1640171537471/5VdHg0lBs.png)

The data we are expecting will look like this, so add this credentials to the form data:

```
{
    "email":"someone@gmail.com",
    "password":"noonecanstopit323"
}

```

![Screen Shot 2021-12-22 at 11.14.12 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1640171674445/PWdlJvQ5WR.png)

 If configuration was successful and you click send, you should get a response like this :

> **{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwZGNkMzI4MzQwMzQ3NzcxZWVhNmUxMyIsImVtYWlsIjoiZG90QGdtYWlsLmNvbSIsImluZm8iOnsiaGFpckNvbG9yIjoiIiwiZmF2b3JpdGVGb29kIjoiIiwiYmlvIjoiIn0sImlzVmVyaWZpZWQiOmZhbHNlLCJpYXQiOjE2MjUwODQ3MTMsImV4cCI6MTYyNTI1NzUxM30.Hadj4ReKFOj46-H7ua7a6yPAhvE8eenLQ9KNwgzfOcE"
}**


What we get back is the token which contains all the data for the new user. 

To verify this we can use the  jwt.io website to parse the JWT token information. You can use jwt.io to experiment with JSON Web Tokens by decoding and encoding them.

So copy the token and paste it in the box and you should see the token decoded!! containing the exact information we signed with jwt. 

![Screen Shot 2021-12-22 at 11.35.09 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1640172963683/iFZ1rSklE.png)

![Screen Shot 2021-12-22 at 11.59.05 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1640174387854/q8a6MukDX.png)

This is how the information will be stored on the client side when the user signs in so they can access it and use it however. 

We can even verify from the mongo shell that the newly created user was saved to the database. 

![Screen Shot 2021-12-23 at 12.16.30 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1640283099112/h23XGRIIQ.png)

### SUMMARY 

Authentication tokens and 2FA play a key role in establishing zero-trust network access control. For example a server could generate a token that has the claim "logged in as administrator" and provide that to a client. The client could then use that token to prove that it is logged in as admin.  


Thanks a lot for coming through! I wish you a merry Christmas and a happy new year in advance! Happy coding.