Skip to main content

Create JWT Token

What is JWT?

JWT (JSON Web Token) is a compact, URL-safe means of representing claims to be transferred between two parties.
The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.

Why use JWT?

JWT is a popular method for securely transmitting information between parties as a JSON object. It is compact, self-contained, and can be easily verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

info

It's basically, take a JavaScript object or whatever you want on it and that JavaScript object, given a secret, which is a string,can deterministically be converted to some random string.
And that string can be converted back to the same object given the same secret.It's almost like hashing but not quite I'm not a scientist so don't quote me on it.
But I can deterministically always get the same result to and from that string in that object if I use the same secret and that's what a JSON Web Token is.

How to create a JWT token

To create a JWT token, you need to follow these steps:

  1. Install the required packages: You need to install the jsonwebtoken package to create and verify JWT tokens. You can do this using npm or yarn.

    npm i jsonwebtoken bcrypt dotenv

    or

    yarn add jsonwebtoken bcrypt dotenv
  2. Create a secret key: You need a secret key to sign the JWT token. This key should be kept secret and not exposed to the client.

  3. Create a function to generate the JWT token: You can create a function that takes the user ID and other claims as input and generates a JWT token.

    const jwt = require("jsonwebtoken");

    const secretKey = "your_secret_key";

    function generateToken(userId) {
    const payload = { id: userId };
    const options = { expiresIn: "1h" }; // Token expiration time
    return jwt.sign(payload, secretKey, options);
    }
  4. Use the function to generate a token: You can call the generateToken function with the user ID to generate a JWT token.

    const userId = "12345"; // Example user ID
    const token = generateToken(userId);
    console.log("Generated JWT Token:", token);
  5. Verify the JWT token: You can create a function to verify the JWT token. This function will check if the token is valid and not expired.

    function verifyToken(token) {
    try {
    const decoded = jwt.verify(token, secretKey);
    return decoded; // Return the decoded payload if the token is valid
    } catch (error) {
    console.error("Invalid token:", error);
    return null; // Return null if the token is invalid
    }
    }
  6. Use the function to verify a token: You can call the verifyToken function with the JWT token to verify it.

Create a new file src/modules/auth and add this:

import jwt from "jsonwebtoken";

export const createJWT = (user) => {
const token = jwt.sign(
{ id: user.id, username: user.username },
process.env.JWT_SECRET
);
return token;
};

This function will take a user and create a JWT from the user's id and username. This is helpful for later when we check for a JWT, we then will know what user is making the request. To do that check, we'll create custom middleware.

export const protect = (req, res, next) => {
const bearer = req.headers.authorization;

if (!bearer) {
res.status(401);
res.send("Not authorized");
return;
}

const [, token] = bearer.split(" ");
if (!token) {
console.log("here");
res.status(401);
res.send("Not authorized");
return;
}

try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.user = payload;
console.log(payload);
next();
return;
} catch (e) {
console.error(e);
res.status(401);
res.send("Not authorized");
return;
}
};

This middleware functions checks for a JWT on the Authorization header of a request. It then attaches the user to the request object before moving on. If anything fails, the user is sent a 401. We need to update our .env file to have a JWT_SECRET. You don't want this secret in your code because it is needed to sign and verify tokens. You can place whatever value you want. Then we need to load in the env file into our environment. Inside of src/index.ts:

import * as dotenv from "dotenv";
dotenv.config();

This will load in our env vars into the process.

Lastly, we need to add our middleware onto our API router to protect it, so inside of src/server.ts, import protect and add it to the chain:

app.use("/api", protect, router);

Now any API call to anthing /api will need to have a JWT. Next we'll create some routes and handlers to create users that are issued JWTs.

We know from our schema that a user needs a unique username and password. Lets create a handler to create a user. Before we can do that, we'll create some helper functions to hash and compare a user's password so we're not storing it in plain text. Inside of src/modules/auth.ts

import * as bcrypt from "bcrypt";

export const comparePasswords = (password, hash) => {
return bcrypt.compare(password, hash);
};

export const hashPassword = (password) => {
return bcrypt.hash(password, 5);
};

comparePasswords compare a plain text password and hashed password to see if they're the same.

hashPassword hashes a password. Now, let's create that handler inside src/handlers/user.ts

import prisma from "../db";
import { createJWT, hashPassword } from "../modules/auth";

export const createNewUser = async (req, res) => {
const hash = await hashPassword(req.body.password);

const user = await prisma.user.create({
data: {
username: req.body.username,
password: hash,
},
});

const token = createJWT(user);
res.json({ token });
};

First thing here is the prisma import. I'm creating module that exports a Prisma client so we don't have to keep creating a new client every time we need it.
There isn't anything special going on here other than creating a new user then using that user to create a JWT and sending that token back as a response. Next, we need to allow a user to sign in.

export const signin = async (req, res) => {
const user = await prisma.user.findUnique({
where: { username: req.body.username },
});

const isValid = await comparePasswords(req.body.password, user.password);

if (!isValid) {
res.status(401);
res.send("Invalid username or password");
return;
}

const token = createJWT(user);
res.json({ token });
};

Using the provided username, we search for a matching user. We'll get more into how to query with Prisma soon. Then we compare passwords. If it's a match, we create a JWT and send it back.

Now we need to create some routes and add these handlers. We can do this in src/server.ts

import { createNewUser, signin } from "./handlers/user";

app.post("/user", createNewUser);
app.post("/signin", signin);
Which is preferable for storing JWT tokens local storage,or secure HTTPS server only cookies?

The preferred method for storing a JWT on a client site for a browser.I've tried like all many different places what I've landed on eventually was just storing it in a cookie.We're not gonna be storing it in a cookie here, but because you need like interaction from the front end and whatnot, but I think starting out with cookies Ideal because cookies are automatically sent up.

  • Storing JWTs in cookies simplifies client-side implementation as cookies are automatically sent with requests.
  • Using the Authorization header on the server side is preferred for better control and clarity.
  • The choice depends on the project context:
    • For client-side simplicity, use cookies.
    • For server-side operations, use Authorization headers. Token is store in localStorage