Skip to main content

Command Palette

Search for a command to run...

Authentication and JWT in Node.js

Updated
5 min read
Authentication and JWT in Node.js

A Complete Guide to Token-Based Authentication, Flow, and Route Protection


Introduction

In modern web applications, not every user should have access to everything. Some routes, data, and operations must be restricted to authenticated users. This is where authentication comes into play.

One of the most popular methods for handling authentication in modern applications is JWT (JSON Web Token). It enables secure and stateless communication between the client and server.

This blog explains authentication, JWT structure, how login works using tokens, and how to protect routes effectively.


What Authentication Means

Authentication is the process of verifying the identity of a user.

Simple Explanation

It answers the question: “Is this user really who they claim to be?”

Example

When a user logs in:

  • They provide credentials (email + password)

  • Server verifies them

  • If valid → user is authenticated


Why Authentication is Required

Without authentication:

  • Anyone could access private data

  • No user-specific features would exist

  • Security would be compromised

Real-World Examples

  • Accessing your email inbox

  • Viewing your bank account

  • Posting on your social media account

Authentication ensures that only authorized users can perform certain actions.


What JWT Is

JWT stands for JSON Web Token. It is a compact, secure way to transmit information between the client and server.

Key Idea

Instead of storing session data on the server, JWT stores user information inside a token that is sent to the client.

  • Stateless (server does not store sessions)

  • Scalable

  • Easy to use in APIs

  • Works well with frontend frameworks


Stateless Authentication (Important Concept)

Traditional authentication uses sessions stored on the server. JWT uses stateless authentication.

What Stateless Means

  • Server does not store user session

  • Client stores the token

  • Each request carries authentication info

Benefit

  • No need to manage sessions

  • Easy to scale across servers


Structure of a JWT

A JWT consists of three parts separated by dots:

Header.Payload.Signature

1. Header

The header contains metadata about the token.

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg → algorithm used

  • typ → token type


2. Payload

The payload contains the actual data.

{
  "userId": "123",
  "email": "user@example.com"
}

Important Note

  • Payload is not encrypted

  • Do not store sensitive data


3. Signature

The signature ensures that the token is valid and not tampered with.

How It Works

  • Header + Payload are combined

  • Signed using a secret key

  • Server verifies it


Login Flow Using JWT

The login process using JWT follows a clear sequence.


Step-by-Step Flow

Client → Login Request → Server → Verify Credentials → Generate Token → Send Token → Client Stores Token

Example Code

import jwt from "jsonwebtoken";

const token = jwt.sign(
  { userId: user._id },
  "secret_key",
  { expiresIn: "1h" }
);

Explanation

  • jwt.sign() creates a token

  • Payload contains user data

  • Secret key signs the token

  • Expiration improves security


Sending Token with Requests

After login, the client must send the token with every request.


Common Method: Authorization Header

Authorization: Bearer <token>

Example Request

fetch("/protected", {
  headers: {
    Authorization: `Bearer ${token}`
  }
});

Why This is Important

  • Server uses token to verify user

  • No need for session storage

  • Enables stateless communication


Protecting Routes Using Tokens

To secure routes, we use middleware that verifies the JWT.


Example Middleware

import jwt from "jsonwebtoken";

function authenticate(req, res, next) {
  const token = req.headers.authorization?.split(" ")[1];

  if (!token) {
    return res.status(401).send("Unauthorized");
  }

  try {
    const decoded = jwt.verify(token, "secret_key");
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(403).send("Invalid token");
  }
}

Using Middleware

app.get("/protected", authenticate, (req, res) => {
  res.send("Protected data");
});

What Happens

  1. Request comes with token

  2. Middleware verifies token

  3. If valid → allow access

  4. If invalid → deny access


JWT Authentication Flow Diagram

Client
  ↓
Login (email/password)
  ↓
Server verifies credentials
  ↓
JWT generated
  ↓
Client stores token
  ↓
Client sends token in requests
  ↓
Server verifies token
  ↓
Access granted or denied

Token Validation Lifecycle

Request →
  Extract Token →
    Verify Signature →
      Decode Payload →
        Allow / Reject Request

Real-World Example

app.post("/login", (req, res) => {
  const user = { id: 1, email: "test@example.com" };

  const token = jwt.sign(user, "secret", { expiresIn: "1h" });

  res.json({ token });
});

Common Mistakes to Avoid

  • Storing sensitive data in payload

  • Not setting token expiration

  • Hardcoding secret keys

  • Not handling invalid tokens


Best Practices

  • Use environment variables for secrets

  • Set expiration time

  • Use HTTPS for secure transmission

  • Validate tokens properly


Key Takeaways

  • Authentication verifies user identity

  • JWT enables stateless authentication

  • Token contains header, payload, signature

  • Client sends token with every request

  • Middleware protects routes


Conclusion

JWT-based authentication is a powerful and scalable way to secure modern applications. By eliminating the need for server-side sessions, it simplifies architecture and improves performance.

Understanding JWT is essential for building secure APIs and real-world backend systems. Once mastered, it becomes a foundational tool in full-stack development.

The next step is to integrate JWT with refresh tokens and role-based access control for production-grade security systems.