JSON Web Tokens (JWT)

A JSON Web Token (JWT) is a compact, self-contained way to securely transmit information between two parties — such as a client and a server — as a JSON object. The information inside the token can be verified and trusted because it is digitally signed.

Think of a JWT like an identity badge at a conference. Once issued at the entrance (login), the badge proves who the person is and what areas they are allowed to access — without them having to show their passport every time they move between rooms. The server gives a token after login, and the client presents that token with every subsequent request.

Why JWT?

Traditional web applications use sessions stored on the server. As applications scaled to millions of users, storing session data on the server became a problem. JWT solves this with a stateless approach — all user information is inside the token itself. The server does not need to store anything. It simply verifies the token's signature when the client sends it.

Structure of a JWT

A JWT consists of three parts separated by dots (.):

xxxxx.yyyyy.zzzzz

These three parts are:

  1. Header — specifies the token type and the algorithm used for signing
  2. Payload — contains the actual data (claims) — who the user is, their role, when the token expires
  3. Signature — verifies that the token has not been tampered with

A Real JWT Example

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjQyLCJuYW1lIjoiUmFqYW4gU2hhcm1hIiwicm9sZSI6InN0dWRlbnQiLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAwMzYwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

This looks like random characters, but it is simply Base64-encoded JSON. It can be decoded by anyone — but not modified without invalidating the signature.

Decoding the Three Parts

Part 1 — Header (decoded)

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

"alg" is the signing algorithm (HS256 = HMAC with SHA-256). "typ" confirms this is a JWT.

Part 2 — Payload (decoded)

{
  "userId": 42,
  "name": "Rajan Sharma",
  "role": "student",
  "iat": 1700000000,
  "exp": 1700003600
}

The payload contains the meaningful data called claims:

  • userId and name — custom claims about the user
  • role — what the user is allowed to do
  • iat — Issued At (when the token was created, as a Unix timestamp)
  • exp — Expiry time (when the token becomes invalid)

Part 3 — Signature

The signature is created by the server using the encoded header, encoded payload, and a secret key. The formula is:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secretKey
)

If anyone modifies even a single character in the token, the signature will no longer match — and the server will reject the token.

How JWT Works in a Login Flow

Step 1: User logs in
Client → POST /login  { "email": "rajan@example.com", "password": "pass123" }

Step 2: Server verifies credentials and creates a JWT
Server → Response: { "token": "eyJhbGci...SflKxw" }

Step 3: Client stores the token (in localStorage or memory)

Step 4: Client sends the token with every protected request
Client → GET /dashboard
         Header: Authorization: Bearer eyJhbGci...SflKxw

Step 5: Server verifies the token signature
         If valid → process the request
         If expired or tampered → return 401 Unauthorized

Standard JWT Payload Claims

ClaimFull NameMeaning
issIssuerWho issued the token (e.g., the website domain)
subSubjectWho the token is about (usually user ID)
audAudienceWho should accept this token
expExpirationUnix timestamp when the token expires
iatIssued AtUnix timestamp when the token was created
nbfNot BeforeToken is not valid before this timestamp
jtiJWT IDA unique identifier for this specific token

Working with JWT in JavaScript (Client Side)

After receiving a token from the server, it can be stored and included in API requests:

// Step 1: Store the token after login
const token = "eyJhbGci...SflKxw";  // received from server
localStorage.setItem("authToken", token);

// Step 2: Use the token in subsequent API requests
const savedToken = localStorage.getItem("authToken");

fetch("https://api.example.com/profile", {
  method: "GET",
  headers: {
    "Authorization": "Bearer " + savedToken
  }
})
  .then(function(response) {
    return response.json();
  })
  .then(function(profile) {
    console.log("Welcome, " + profile.name);
  });

Decoding a JWT Without Verification (Inspect Payload)

The payload of a JWT can be decoded in JavaScript without any library — simply by splitting the token and decoding the Base64 part:

function decodeJWT(token) {
  const payload = token.split(".")[1];
  const decoded = atob(payload);
  return JSON.parse(decoded);
}

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjQyLCJuYW1lIjoiUmFqYW4iLCJyb2xlIjoic3R1ZGVudCJ9.signature";

const data = decodeJWT(token);
console.log(data.name);    // Output: Rajan
console.log(data.role);    // Output: student

Important: This decodes the payload but does not verify the signature. Signature verification must only happen on the server using the secret key. Never trust decoded JWT data from the client alone without server-side verification.

Security Best Practices for JWT

  • Always set an expiry time (exp) on tokens — avoid tokens that never expire
  • Keep the secret key on the server only — never expose it in client-side code
  • Do not store sensitive information like passwords or bank details in the JWT payload — it is encoded, not encrypted
  • Use HTTPS always — JWT tokens sent over plain HTTP can be intercepted
  • Prefer storing tokens in memory or httpOnly cookies over localStorage for better security against XSS attacks
  • Implement token refresh flows for long-lived sessions instead of long expiry times

JWT vs Traditional Sessions

FeatureJWT (Stateless)Server Sessions (Stateful)
Server StorageNot required — token is self-containedRequired — session stored in server memory or DB
ScalabilityHighly scalable — works across multiple serversHarder to scale — sessions tied to one server
Token RevocationDifficult — token is valid until expiryEasy — delete the session from server
Mobile App SupportExcellent — stateless works well for appsRequires cookies, less mobile-friendly
Payload VisibilityPayload is readable (Base64 encoded)Session data stays private on server

Online JWT Tool

The official JWT debugger at jwt.io allows pasting any JWT token and instantly seeing the decoded header, payload, and whether the signature is valid. It is extremely useful for learning, testing, and debugging JWT-based authentication.

Key Points to Remember

  • JWT is a compact token format used for authentication and information exchange
  • It has three parts: Header, Payload, and Signature — separated by dots
  • The payload contains JSON claims — user identity, role, and expiry time
  • JWT is stateless — the server does not need to store session data
  • The payload is encoded (readable), not encrypted — never store sensitive data in it
  • Always verify JWT on the server using the secret key before trusting its data
  • Use jwt.io to inspect and debug JWT tokens

Summary

JSON Web Tokens are the backbone of modern authentication in web and mobile applications. Built entirely on JSON, they provide a secure, scalable, and stateless method for verifying user identity across API requests. Understanding how JWT is structured and how it flows between client and server gives a strong foundation for building secure, real-world applications.

Leave a Comment

Your email address will not be published. Required fields are marked *