Back to Blog
tutorials

JWT Explained: What It Is and Why Developers Use It

JWT keeps you logged in without the server remembering anything. Here is exactly how JSON Web Tokens work, what is inside them, and when to use them.

Curious Adithya7 min read

Every time you log into an app and stay logged in, something is keeping track of who you are. Either the server is remembering you, or you are carrying proof of who you are in your pocket.

JWT is the second approach. And once you understand how it works, a lot of authentication concepts click into place.

Authentication vs Authorization: The Difference That Matters

Before we get into JWT itself, let us clear up a common confusion.

Authentication is proving who you are. You send a username and password, the server checks if they match, done. You are authenticated.

Authorization is proving you still are who you say you are, every time you make a request after logging in. The server needs to know: is this the same person who logged in 10 minutes ago? Are they allowed to access this resource?

JWT is purely an authorization tool. It handles what happens after login, not the login itself.

The Old Way: Sessions and Cookies

Here is how traditional session-based auth works.

  1. You log in. Server verifies your credentials.
  2. Server creates a session in its memory, tied to your user account.
  3. Server generates a unique session ID and sends it to your browser as a cookie.
  4. Every request your browser makes includes that cookie.
  5. Server looks up the session ID in memory, finds your user, and decides if you are authorized.

Simple enough. But there is a problem buried in step 5.

The server has to store your session in its memory. If you have two servers running (say, for load balancing), and your next request goes to a different server, that server has no record of your session. You get logged out. Or worse, it quietly fails and you spend 20 minutes wondering why nothing is working.

Scale this to microservices, multiple regions, or any distributed architecture and you have a genuine headache.

This is exactly the problem JWT was built to solve.

The JWT Way: Stateless Authorization

With JWT, here is what changes.

  1. You log in. Server verifies your credentials.
  2. Server creates a JSON Web Token that contains your user information directly.
  3. Server signs that token with a secret key it holds.
  4. Server sends the token to the client (browser, mobile app, whatever).
  5. Every request includes that token.
  6. Server verifies the token's signature, reads the user info from the token itself, and authorizes you.

Notice what is different. The server stores nothing. All the user information lives inside the token, on the client. The server just needs its secret key to verify that the token has not been tampered with.

No session memory. No database lookup. No server-side state at all.

The server remembers nothing. The token remembers everything.

What Is Actually Inside a JWT

A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkaXRoeWEiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

See those two periods? They divide the token into exactly three parts.

Part 1: The Header

Base64 encoded. Contains the algorithm being used for signing and the token type.

{
  "alg": "HS256",
  "typ": "JWT"
}

Part 2: The Payload

Base64 encoded. This is where your actual data lives.

{
  "sub": "1234567890",
  "name": "Adithya",
  "iat": 1516239022,
  "exp": 1516242622
}
  • sub is the subject, usually the user's ID
  • iat is "issued at", the timestamp when the token was created
  • exp is "expires at", when the token becomes invalid
  • You can add any custom fields you need

Part 3: The Signature

This is the critical piece. The server takes the base64-encoded header, adds a period, adds the base64-encoded payload, and runs the whole thing through a hashing algorithm (like HMAC SHA-256) using its secret key.

The result is the signature.

HMACSHA256(
  base64(header) + "." + base64(payload),
  secret_key
)

When a request comes in with a JWT, the server repeats this exact process with the header and payload it received. If the signature it calculates matches the signature in the token, the data has not been touched. If it does not match, someone tried to tamper with the token and the server rejects it.

This is the magic. You can read the payload (it is just base64, anyone can decode it). But you cannot change it without invalidating the signature. And you cannot forge a valid signature without the server's secret key.

Why Expiry Matters

Look at that exp field again. Tokens must expire.

Here is why. If someone steals a JWT (say, from an insecure client), they can use it to authenticate as you. If the token never expires, they can use it forever. An expiry time (commonly 15 minutes to 1 hour for access tokens) limits the damage window.

Most implementations pair short-lived access tokens with longer-lived refresh tokens. The access token expires quickly. The refresh token (stored more securely) is used to get a new access token without making the user log in again.

The Real Power: Multiple Servers

Here is the scenario where JWT completely outclasses session-based auth.

Imagine a bank that runs two separate services: one for banking, one for retirement accounts. They want users who are logged in on the banking site to be automatically logged in on the retirement site without re-entering credentials.

With sessions? Impossible without a shared session store (which adds its own complexity and becomes a single point of failure).

With JWT? Both servers share the same secret key. The client sends the same JWT to both. Both can verify it independently. Done. The user is authorized on both systems from a single login.

This extends to microservices. You can have ten different services. As long as they all share the secret key, any JWT the client holds works across all of them. No central session store. No coordination overhead between services.

This is why JWT became the standard for APIs and microservice architectures.

One Important Caveat

Because JWT is stateless, you cannot invalidate a token before it expires. If a user logs out, you cannot "delete" their JWT the way you would delete a session. The token stays valid until expiry.

Work-arounds exist. Token blocklists (storing invalid tokens), very short expiry times, or rotating secrets. But none of them are as clean as session invalidation.

JWT is excellent for APIs and distributed systems. For traditional web apps where you need instant logout and full session control, sessions still have a place.

The tool matches the use case. Know which one fits what you are building.

How to Use It in Code

Most languages have solid JWT libraries. In Node.js, jsonwebtoken is the standard.

const jwt = require('jsonwebtoken');

// Creating a token after login
const token = jwt.sign(
  { userId: user._id, email: user.email },
  process.env.JWT_SECRET,
  { expiresIn: '1h' }
);

// Verifying a token on incoming requests
try {
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  // decoded.userId, decoded.email are available
} catch (err) {
  // Token is invalid or expired
  res.status(401).json({ error: 'Unauthorized' });
}

Two things to note. Your JWT_SECRET must be stored in an environment variable, never hardcoded. And always wrap verify in a try-catch because it throws if the token is invalid or expired.

What You Should Take Away

  • JWT stands for JSON Web Token. It is used for authorization, not authentication.
  • Traditional sessions store user info on the server. JWT stores it in the token on the client.
  • A JWT has three parts: header (algorithm), payload (your data), signature (tamper protection).
  • The signature is created using a secret key. Without the key, you cannot forge a valid token.
  • JWT is stateless. The server stores nothing. This makes it perfect for APIs, microservices, and distributed systems.
  • Tokens must expire. Pair short-lived access tokens with longer-lived refresh tokens.
  • The one downside: you cannot instantly invalidate a JWT. Plan for this in your design.

Once you get JWT, you will see it everywhere. Every API you work with, every modern auth system, every mobile backend. It is one of those concepts that pays dividends for your entire career.

Written by Curious Adithya for Art of Code.