Skip to Content
Lesson 2

Session and Token-Based Authentication

Now that you have a secure User model, the next task at “Innovate Inc.” is to manage a user’s state after they log in. How does the application remember who they are as they navigate from one page to another? Your senior developer has asked you to investigate and implement a modern, stateless authentication strategy that will scale well as the company grows. This lesson explores two common approaches: traditional sessions and modern token-based authentication with JSON Web Tokens (JWTs).


Stateful vs. Stateless Authentication

At the heart of managing user logins is the concept of “state.” Does the server need to remember (keep a stateful record of) every logged-in user, or can it operate without that memory (statelessly)?

Stateful Authentication (Sessions)

The traditional approach is to use sessions. Here’s how it works:

  1. A user logs in with their credentials.
  2. The server verifies the credentials and creates a “session” record, storing it in server memory or a database.
  3. The server sends a unique Session ID back to the user’s browser, which stores it in a cookie.
  4. On every subsequent request, the browser sends the Session ID cookie back to the server.
  5. The server looks up the session record using the ID to identify the user and their permissions.

Pros: Session data is stored on the server, making it secure from client-side tampering.

Cons: This model struggles with scalability. If you have multiple servers, you need a shared session store so that any server can handle any user’s request. This adds complexity and a potential single point of failure.

Stateless Authentication (Tokens)

The modern, preferred approach for APIs is stateless authentication using tokens.

  1. A user logs in with their credentials.
  2. The server verifies the credentials and generates a token — a self-contained string of data containing user information.
  3. This token is “signed” with a secret key known only to the server.
  4. The server sends the token back to the client. The client is responsible for storing it (e.g., in localStorage).
  5. On every subsequent request, the client includes the token in the Authorization header.
  6. The server receives the token, verifies its signature to ensure it hasn’t been tampered with, and then reads the user information from it. The server does not need to look up anything in a database to identify the user.

Pros: Highly scalable, as any server with the secret key can verify a token. It’s “stateless” because the server doesn’t need to store any session information.

Cons: Tokens must be stored securely on the client. Since they are self-contained, they cannot be easily revoked before their expiration.

For the “Innovate Inc.” portal, a stateless JWT approach is the best choice.


Introduction to JSON Web Tokens (JWTs)

A JSON Web Token (JWT) is a compact, URL-safe standard for creating access tokens. A JWT is just a string with three parts, separated by dots (.):

xxxxx.yyyyy.zzzzz

  • Header: Metadata about the token, such as the signing algorithm.
  • Payload: The data “claims” about the user (e.g., user ID, username).
  • Signature: A cryptographic signature to verify the token’s integrity.

JWT Decoded

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header:
{
  “alg”: “HS256”,
  “typ”: “JWT”
}

Payload:
{
  “sub”: “1234567890”,
  “name”: “John Doe”,
  “iat”: 1516239022
}

Signature:
HMACSHA256(
  base64UrlEncode(header) + ”.” +
  base64UrlEncode(payload),
  your-256-bit-secret
)

The Payload contains the claims. These are statements about an entity (typically, the user) and additional data. Do not put sensitive information in the payload, as it is only Base64Url encoded, not encrypted, and can be easily read by anyone. The signature ensures that the payload has not been tampered with.


Implementing JWTs with jsonwebtoken

The jsonwebtoken library is the standard for working with JWTs in Node.js.

First, install it:

npm install jsonwebtoken

Creating (Signing) a Token

When a user successfully logs in, you create a token for them. The jwt.sign() method takes a payload, a secret key, and optional settings (like an expiration time).

import jwt from 'jsonwebtoken'; const secret = process.env.JWT_SECRET; const expiration = '2h'; // Token will be valid for 2 hours // In your user login logic... const user = await User.findOne({ email: req.body.email }); // ... (password verification logic) ... // The payload should contain non-sensitive user data const payload = { _id: user._id, username: user.username, email: user.email, }; const token = jwt.sign({ data: payload }, secret, { expiresIn: expiration }); // Example token: eyJhbGciOi... // Send the token back to the client res.json({ token, user });

Verifying a Token

When the client sends the token back with a request, you need to verify it. The jwt.verify() method checks the signature and expiration. If the token is invalid or expired, it will throw an error.

import jwt from 'jsonwebtoken'; const secret = process.env.JWT_SECRET; // In a middleware function that protects routes... let token = req.headers.authorization; // "Bearer <token>" if (token) { token = token.split(' ').pop().trim(); } if (!token) { return res.status(401).json({ message: 'No token found!' }); } try { // The decoded object will contain the original payload ({ data: { _id, ... } }) const { data } = jwt.verify(token, secret, { maxAge: '2h' }); req.user = data; // Attach user data to the request object } catch { return res.status(401).json({ message: 'Invalid token!' }); } // Proceed to the next middleware or route handler next();

Activity 1: Create and Decode a JWT

  1. In a new Node.js script, install and import jsonwebtoken.
  2. Define a sample payload object with some user data (e.g., _id, username).
  3. Define a secret key string.
  4. Use jwt.sign() to create a token that expires in 1h. Log the token to the console.
  5. Use jwt.verify() with the correct secret to decode the token. Log the decoded payload.
  6. Try to verify the token with the wrong secret inside a try...catch block. Log the error to see what jsonwebtoken returns for an invalid signature.

Knowledge Check

What is the primary advantage of token-based (stateless) authentication over session-based (stateful) authentication?

  • Select an answer to view feedback.

Which part of a JWT ensures that the token has not been tampered with?

  • Select an answer to view feedback.

Where should you store highly sensitive information, like a user's credit card number?

  • Select an answer to view feedback.

Summary

In this lesson, you explored the differences between stateful (session-based) and stateless (token-based) authentication, concluding that JWTs are the superior choice for modern, scalable applications. You learned that a JWT is a signed, self-contained token composed of a header, payload, and signature. We used the jsonwebtoken library to create (sign) and validate (verify) tokens, which are the fundamental operations for securing an API. You are now prepared to protect API endpoints by requiring a valid JWT for access.


References

Additional Resources