Auth & Security/
Lesson

When you fill in a login form on a classic web app and the page just "remembers" you on every subsequent click, that's sessionWhat is session?A server-side record that tracks a logged-in user. The browser holds only a session ID in a cookie, and the server looks up the full data on each request.-based auth doing its job quietly in the background. It's the oldest pattern in web development, and it still fits a huge proportion of projects perfectly. This lesson breaks down how it works and shows you what a solid Express implementation looks like.

How sessions work

Think of a sessionWhat is session?A server-side record that tracks a logged-in user. The browser holds only a session ID in a cookie, and the server looks up the full data on each request. like a coat-check ticket. When you arrive at a venue you hand over your coat (your credentials), and the attendant gives you a small numbered ticket (the session ID). Every time you want your coat back you show the ticket, you don't carry the coat around with you all evening.

The flow is:

  1. User sends their credentials (email + password).
  2. Server verifies them, then creates a session record containing the user's ID and any other data you need.
  3. Server stores the session in a session store (Redis, a database, or even a file in development).
  4. Server sends a cookieWhat is cookie?A small piece of data the browser stores and automatically sends with every request to the matching server, often used for sessions. to the browser containing only the session ID, nothing sensitive.
  5. On every subsequent request the browser sends the cookie automatically.
  6. Server reads the session ID from the cookie, fetches the matching record from the store, and knows who the user is.

The key detail: the cookie is opaque. It is just a random identifier. Your user data never leaves the server.

Gotcha
If you run multiple server instances (load balancing), every instance must be able to reach the same session store. Without this, a user might hit a different server that has no record of their session and get logged out at random. Redis is the standard solution here.
02

Setting up express-sessionWhat is session?A server-side record that tracks a logged-in user. The browser holds only a session ID in a cookie, and the server looks up the full data on each request.

npm install express-session
npm install connect-redis  # or connect-pg-simple for PostgreSQL
import session from 'express-session';
import RedisStore from 'connect-redis';
import { createClient } from 'redis';

const redisClient = createClient();
redisClient.connect();

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,  // Signs the cookie to prevent tampering
  resave: false,                       // Don't re-save unchanged sessions
  saveUninitialized: false,            // Don't create a session until something is stored
  name: 'sessionId',                  // Rename from the default 'connect.sid'
  cookie: {
    secure: process.env.NODE_ENV === 'production',  // HTTPS only in production
    httpOnly: true,                   // JavaScript cannot read this cookie
    maxAge: 24 * 60 * 60 * 1000,      // 24 hours in milliseconds
    sameSite: 'strict'                // Blocks cross-site request forgery
  }
}));

Two options deserve special attention. saveUninitialized: false prevents the server from creating a session record for every anonymous visitor, important for privacy regulations and for keeping your session store lean. httpOnly: true means that even if an attacker injects malicious JavaScript into your page, that script cannot read the session cookieWhat is cookie?A small piece of data the browser stores and automatically sends with every request to the matching server, often used for sessions.. Always set it.

Edge case
sameSite: 'strict' blocks the cookie from being sent with cross-origin requests entirely. If your frontend and API live on different domains (a common SPA setup) you may need sameSite: 'none' paired with secure: true, but that opens CSRF exposure you then need to handle separately with a CSRF token.
03

AuthenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. routes

// Login
app.post('/auth/login', async (req, res) => {
  const { email, password } = req.body;

  const user = await db.findUserByEmail(email);
  if (!user || !await verifyPassword(password, user.passwordHash)) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Attach data to the session - this triggers a save to the store
  req.session.userId = user.id;
  req.session.email = user.email;

  res.json({ message: 'Login successful', user: { id: user.id, email: user.email } });
});

// Logout - destroy the server-side record, then clear the cookie
app.post('/auth/logout', (req, res) => {
  req.session.destroy();
  res.clearCookie('sessionId');
  res.json({ message: 'Logout successful' });
});

// Protected route
app.get('/profile', requireAuth, async (req, res) => {
  const user = await db.findUserById(req.session.userId);
  res.json(user);
});

Notice that logout calls req.session.destroy() on the server before clearing the cookieWhat is cookie?A small piece of data the browser stores and automatically sends with every request to the matching server, often used for sessions.. This matters: if you only clear the cookie on the client, someone who captured the sessionWhat is session?A server-side record that tracks a logged-in user. The browser holds only a session ID in a cookie, and the server looks up the full data on each request. ID earlier could still use it. Destroying the record server-side invalidates the ID immediately, which is one of the biggest advantages sessions have over JWTs.

04

Auth middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it.

Every protected route needs to verify that a valid sessionWhat is session?A server-side record that tracks a logged-in user. The browser holds only a session ID in a cookie, and the server looks up the full data on each request. exists before doing any real work. A simple middleware handles this in one place so you never have to repeat the check:

function requireAuth(req, res, next) {
  if (!req.session.userId) {
    return res.status(401).json({ error: 'Authentication required' });
  }
  next();
}

Attach it to any route or router: app.get('/dashboard', requireAuth, handler). If the session is missing or expired, the user gets a 401 and the handler never runs.

05

Quick reference

OptionRecommended valueWhy
secretLong random string from envSigns the cookie; never hardcode
resavefalseAvoids unnecessary store writes
saveUninitializedfalseDon't create sessions for anonymous visitors
cookie.httpOnlytruePrevents XSS from stealing the cookie
cookie.securetrue in productionSends cookie over HTTPS only
cookie.sameSite'strict' (same-origin) or 'none' (cross-origin)CSRF mitigation
cookie.maxAge24h–7d depending on appSets session lifetime