Back to List

Sessions and Authentication β€” Express Session

Learn how session-based authentication works in Express, the relationship with cookies, and the concept of session stores.

Intermediate
|
12min
|
Verified (2026-07)
sessionexpress-sessionauthenticationloginsession store
Progress0/55 (0%)

Sessions and Authentication β€” Express Session

After completing this topic

You will understand why sessions are necessary, be able to implement login/logout using express-session, and know the concept of session stores and security settings.


Why are sessions necessary?

In the Cookies topic, we learned that "HTTP is stateless." While cookies can be used to maintain state, they have critical limitations.

javascript
// Bad β€” Storing user information directly in a cookie
res.cookie('user', JSON.stringify({ id: 1, role: 'admin' }));

The client (browser) can freely modify cookies. What if a user changes role: 'admin' to role: 'superadmin' and sends it? The server cannot distinguish this.

Sessions solve this problem. Sensitive data is stored on the server, and only an identifier key (session ID) is sent to the client.

text
Cookie method: Stores "name=Hoon, role=admin" on the client (vulnerable to tampering)
Session method: Stores only "sessionID=abc123" on the client β†’ Server retrieves user information using abc123

How sessions work

text
1. Login request
   Client β†’ Server: POST /login {username: "Hoon", password: "****"}

2. Server creates a session
   Server: Stores {userId: 1, role: "admin"} in the session store
   Server: Generates session ID = "abc123"
   Server β†’ Client: Set-Cookie: connect.sid=abc123

3. Subsequent requests
   Client β†’ Server: GET /dashboard (Cookie: connect.sid=abc123)
   Server: Retrieves session using abc123 β†’ Verifies {userId: 1, role: "admin"}
   Server β†’ Client: Dashboard data

The client only knows the session ID, and the actual data (userId, role) is stored in the server's memory or database. Even if the client forges the session ID, authentication will fail if the corresponding ID does not exist in the server's session store.


Installing and configuring express-session

bash
npm install express-session
javascript
const express = require('express');
const session = require('express-session');
const app = express();

app.use(express.json());
app.use(session({
  secret: 'my-secret-key-change-this',
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,
    secure: false,      // true in production
    maxAge: 3600000      // 1 hour
  }
}));
OptionDescription
secretA key to encrypt the session ID. Should be long and random
resaveWhether to save the session on each request, even if there are no changes. false is recommended
saveUninitializedWhether to save empty sessions. Setting to false prevents cookies from being issued before login
cookieSession cookie options (httpOnly, secure, maxAge, etc.)

secret should never be hardcoded into the code. It should be managed as an environment variable:

javascript
secret: process.env.SESSION_SECRET || 'fallback-dev-only'

Implementing login/logout

javascript
const users = [
  { id: 1, username: 'alice', password: 'pass123', role: 'admin' },
  { id: 2, username: 'bob', password: 'pass456', role: 'user' }
];

// Login
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);

  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Store user information in the session
  req.session.userId = user.id;
  req.session.role = user.role;

  res.json({ message: `Welcome, ${user.username}` });
});

// Logout
app.post('/logout', (req, res) => {
  req.session.destroy(err => {
    if (err) {
      return res.status(500).json({ error: 'Logout failed' });
    }
    res.clearCookie('connect.sid');
    res.json({ message: 'Logged out' });
  });
});

req.session is an object. You can freely store any properties in it. req.session.destroy() deletes the session data on the server.


Authentication middleware

When accessing protected routes, you need to check "Is the user logged in?" every time. Creating this as middleware allows you to reuse it in all routes.

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

function requireAdmin(req, res, next) {
  if (req.session.role !== 'admin') {
    return res.status(403).json({ error: 'Admin access required' });
  }
  next();
}

// Usage
app.get('/profile', requireAuth, (req, res) => {
  res.json({ userId: req.session.userId, role: req.session.role });
});

app.get('/admin/dashboard', requireAuth, requireAdmin, (req, res) => {
  res.json({ message: 'Admin dashboard' });
});

You can chain middleware to create complex conditions like "login + admin".


Session store β€” Memory vs. external storage

By default, express-session stores sessions in server memory. This is sufficient for development, but it presents problems in production.

ProblemDescription
Server restartMemory is initialized, so all users are logged out
Memory leakAs sessions accumulate, the server's memory becomes insufficient
Multiple serversServer A's memory-stored session is not known to Server B

In production, use external storage such as Redis, MongoDB, or PostgreSQL:

bash
npm install connect-redis redis
javascript
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');

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

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false
}));

Storing sessions in Redis ensures that sessions are maintained even when the server restarts, and if multiple servers share the same Redis, they can share sessions.


Session vs. JWT β€” When to use what

SessionJWT
State storageServer (stateful)Client (stateless)
ScalingRequires a shared store like RedisNo server-side sharing needed
LogoutInvalidated immediately with destroy()Valid until token expiration (requires a blacklist)
SecurityServer manages dataDifficult to handle token theft
Suitable casesTraditional web apps, admin pagesAPI servers, mobile apps, microservices
text
Traditional web app (SSR) β†’ Sessions are more natural
SPA + API server β†’ JWT is more convenient
Mobile app β†’ JWT (Cookie management is complex)

Key takeaways

ConceptSummary
SessionStores user data on the server and sends only an ID to the client
Session IDAn identifier key sent in a cookie (connect.sid)
req.sessionAn object for reading and writing session data
destroy()Logout β€” Deletes session data
Session storeUse external storage like Redis in production

The key to session-based authentication: Store sensitive data on the server, and send only the key (session ID) to the client. Understanding this principle will naturally lead to an understanding of the differences with token-based authentication (JWT).


πŸ’¬ Questions & Comments

0 comments

You can post without signing in. Guest comments cannot be edited or deleted by their author.

0/2000

Loading...