An essential component of every contemporary web application is authentication. You need a safe method of user verification whether you're developing a mobile backend, SaaS solution, or REST API. JWT (JSON Web Token) authentication is currently one of the most used methods. It enables stateless, secure user identity transmission between client and server.

In this post, we will use Node.js to construct JWT-based authentication in a straightforward and useful manner.

What is JWT?
JWT (JSON Web Token) is a compact, URL-safe token format used for authentication and authorization.

A JWT typically consists of three parts:

  • Header
  • Payload
  • Signature

Real-world analogy: Think of JWT like a digital ID card. Once issued, the client carries it and shows it to access protected resources.

Install Dependencies

First, install the required packages:
npm install express jsonwebtoken bcryptjs


Project Setup
Basic Express setup:
const express = require("express");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");

const app = express();
app.use(express.json());

const SECRET_KEY = "yourSecretKey";


Create JWT Token
This function generates a token when the user logs in successfully:
function generateToken(user) {
  return jwt.sign({ id: user.id }, SECRET_KEY, { expiresIn: "1h" });
}


Login Route
Here we validate user credentials and generate a token:
app.post("/login", async (req, res) => {
  const { email, password } = req.body;

  // Simulated user (in real apps, fetch from DB)
  const user = { id: 1, email, password: "123456" };

  if (password !== user.password) {
    return res.status(401).send("Invalid credentials");
  }

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


Secure Password (Recommended Improvement)
Instead of storing plain passwords, use hashing:
const hashedPassword = await bcrypt.hash("123456", 10);
const isMatch = await bcrypt.compare(password, hashedPassword);


Middleware to Verify Token
This middleware protects routes by verifying JWT:
function authMiddleware(req, res, next) {
  const token = req.headers["authorization"];

  if (!token) return res.sendStatus(403);

  jwt.verify(token, SECRET_KEY, (err, decoded) => {
    if (err) return res.sendStatus(401);
    req.user = decoded;
    next();
  });
}

Protected Route Example
app.get("/profile", authMiddleware, (req, res) => {
  res.json({ message: "Protected data", user: req.user });
});

Step-by-Step Flow

  • User logs in with email and password
  • Server validates credentials
  • Server generates JWT token
  • Client stores token (localStorage or cookies)
  • Client sends token in Authorization header
  • Server verifies token before giving access

Real-World Example
Imagine a shopping app:

  • Without JWT: Server checks login every time (slow and heavy)
  • With JWT: User logs in once and reuses token for all requests (fast and scalable)

Advantages of JWT Authentication

  • Stateless (no session storage needed)
  • Scalable for large applications
  • Works well with APIs and microservices
  • Easy integration with frontend frameworks

Disadvantages

  • Token cannot be easily revoked (unless using blacklist)
  • Larger payload compared to session IDs
  • Requires careful handling of secret keys

Best Practices

  • Always store SECRET_KEY in environment variables
  • Use HTTPS to protect tokens
  • Set token expiration time
  • Use refresh tokens for long sessions
  • Avoid storing sensitive data in JWT payload

Conclusion
You now have a working JWT authentication system in Node.js. By combining Express, JWT, and secure password handling, you can build scalable and secure authentication for real-world applications. Start implementing this in your projects and gradually enhance it with refresh tokens, role-based access, and database integration.