Implenting JWT's from scratch.
Tags: webdev, node, javascript
Published: 2022-10-07 16:53:40

Jsonwebtokens or JWT as they are otherwise known is an open standard for creating a data signature that contains a payload in JSON format. A JWT is signed with a private key and/or public key and is URL-safe, this implies that a JWT doesn't have unencoded `/` or `?` or `&` characters. A JWT is used for authorization and lightweight authentication. The payload in a JWT will usually contain one or more claims that define the level of authorization allocated to the user.
In this article, we will see a basic implementation of JWTs to understand how they work under the hood. We will move to use a standard JWT library and practical applications JWTs in an application. Visit [Netcreed](https://thenetcreed.com) to read more articles like this
## Creating a JWT
You should know that a JWT is an implementation of an HMAC; Hash-Based Message Authentication Code. To create one we need the crypto module that comes baked in with Node JS, the crypto module exposes a function `createHmac` function that allows us to create one. This method accepts two parameters, a hashing algorithm and a secrete, This secrete is what is used to validate the authenticity of our JWT. The secrete can be shared between a client and server if need be, but most of the time it should just be kept on the server. Let's write the function that allows us to create a signature.
```typescript
import { createHmac } from 'crypto';
type Payload = Record<string, any>;
type Header = {
 alg: string,
 type: string
};
const createSignature = (header: string, secrete: string, payload: string) => {
 return createHmac('sha256', secrete)
 .update(header + '.' + payload) 
 .digest('base64');
}
export default function Sign (secrete: string, payload: Payload) {
 const header = {
 alg: "HS256",
 type: "JWT"
 };
const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64');
 const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64');
 const signature = createSignature(encodedHeader, secrete, encodedPayload);
return `${encodedHeader}.${encodedPayload}.${signature}`;
}
console.log(Sign("kalashin", { name: 'kinanee'}));
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoia2luYW5lZSJ9.a5koH-eUf0JXa_eX-CjriIp-Wta51ftpwqr8rRJeRF8
```
From the code block above, we can decipher that a JWT has three parts, the encoded header, the encoded payload, and the signature, if you are familiar with your javascript you know that the information in the header and the payload can easily be constructed. Thus it is not wise to store any sensitive information in the payload. The signature is created by calling the `createHmac` function, we use the `sha256` hashing algorithm, this information should also be contained in the `alg` property of the header, however `sha256` is not the only algorithm that can be used, JWTs support other hashing algorithms. We call update on the `HMAC` object returned from the `createHmac` function to add the data we want to store in it, when we are done we call `digest` on it. The digest function will consume the `HMAC` and return to us the calculated digest of the value passed into the `update` function in an encoded string in the format passed into it, in this example we use the `base64url` to ensure that our JWT is `URL-Safe`.
## Verifying a JWT
We have created our JWT, how can we now verify and decode the token when it is passed between the server and the client?
```typescript
import { createSignature } from './sign';
export const verify = (token: string, secrete: string) => {
 const parts = token.split('.')
if (parts.length !== 3) {
 throw Error('Invalid token')
 }
 const [encodedHeader, encodedPayload, signature] = parts
 const createdSignature = createSignature(encodedHeader, secrete, encodedPayload)
 if (signature !== createdSignature) {
 throw Error("Invalid signature")
 }
 return true;
}
```
First, we split the token string into three parts, when we created the token remember we attached the encoded headers to the encoded payload and finally to the signature using a period as a delimiter. Now we use the split method on the string to separate each part of the token, and an array with three items is returned to us. Given the same value and the same secrete, the same digest will be obtained from an HMAC object, thus we can destructure the array to obtain the values needed to reconstruct the signature then we compare the newly generated signature to the signature in the JWT and if there is a match we return true. I decided against decoding the token here, and instead, i just return true, however, let's write the script to decode the token.
## Decoding JWT Payload
Most times we are interested in decoding the values stored in the token after verifying it, that is quite simple to do. We need to convert the token to an array of encoded values like we did when verifying the token, we obtain the encoded payload, then we create a `base64url` buffer from it, decode the buffer to JSON then convert it back to the payload object.
```typescript
export const decode = (token: string) => {
 const parts = token.split('.')
 if (parts.length !== 3) {
 throw Error('Invalid jwt');
 }
 const payload = Buffer.from(parts[1].toString(), 'base64url').toString('utf8')
 return JSON.parse(payload)
}
```
We have seen a basic implementation of JWT using NodeJS. let us now look at using a more robust solution, we will implement a JWT using the `jsonwebtoken` library from `npm`. This is an advanced solution for jwt implementation, allows us to set an expiry on our token, and allows us to use other hashing algorithms.
## Using jsonwebtoken
To install the library run `npm i jsonwebtoken`, this command installs the library from npm for us, we can then export helper functions that will sign, verify and decode the token for us.
```typescript
import jwt from 'jsonwebtoken';

export const sign = function (secrete: string, data: any) {
 return jwt.sign({
 data,
 }, secrete, { expiresIn: 60 * 60 });
}
export const verify = function (token: string, secrete: string) {
 return jwt.verify(token, secrete);
}
export const decodeToken = function (token: string) {
 return jwt.decode(token);
}
```
You will agree with me that the library implementation leaves you with a lot less to worry about, a practical application of this would be to check if a particular user is authorized to access a particular route in your application. Let's see a classical example.
```typescript
import { sign } from './helper'
const payload = { type: 'ADMIN', id: 1};
const jwt = sign('1234', payload);
```
We have created a payload with a type property of admin and this represents an admin in your system, thus whenever a call is made to any part of your app that requires admin access, you can write a middleware that will be attached to that route. It will decode the token and verify if the user has the right access.
```typescript
import { verify } from './helper'
const isAdmin = (req: Request, res: Response, next: NextFunction) => {
 const token = req.headers.authorization.split(' ')[1];
 if (!token) res.status(401).end('Not auth token');
 const payload = verify(token, '1234');
 if (payload.type !== 'ADMIN') res.status(401).end('Not authorized');
 next();
}
```
That's it for today, to read more articles like this visit [Netcreed](https://thenetcreed.com) to read more articles like this