TrueSpec

Secure Session Management with Redis

Express sessions with Redis store, expiration, and fingerprinting

What You’ll Build

After following this guide, you will have a working implementation of secure session management with redis in your project. Replace in-memory Express sessions with Redis-backed sessions for production. Includes secure cookie configuration, session expiration, browser fingerprinting to prevent session hijacking, and automatic cleanup of expired sessions.

Use Cases & Problems Solved

  • Protect routes so only authenticated users can access sensitive pages
  • Allow users to sign up, log in, and manage their accounts securely
  • Avoid storing raw passwords or building session management from scratch

Prerequisites

  • Redis server running (local or cloud)
  • Express.js project
  • Node.js 18+

Step-by-Step Implementation

Install dependencies

The following snippet shows how to install dependencies. Copy this into your project and adjust the values for your environment.

npm install express-session connect-redis ioredis

Configure Redis session store

The following snippet shows how to configure redis session store. Copy this into your project and adjust the values for your environment.

const session = require('express-session');
const RedisStore = require('connect-redis').default;
const Redis = require('ioredis');

const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');

app.use(session({
  store: new RedisStore({ client: redis }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    sameSite: 'strict',
    maxAge: 24 * 60 * 60 * 1000, // 24 hours
  },
}));

Session usage in routes

The following snippet shows how to session usage in routes. Copy this into your project and adjust the values for your environment.

// Set session data after login
app.post('/login', async (req, res) => {
  const user = await authenticateUser(req.body);
  req.session.userId = user.id;
  req.session.userAgent = req.headers['user-agent']; // fingerprint
  res.json({ message: 'Logged in' });
});

// Check session with fingerprint validation
app.get('/profile', (req, res) => {
  if (!req.session.userId) return res.status(401).json({ error: 'Not logged in' });
  if (req.session.userAgent !== req.headers['user-agent']) {
    req.session.destroy(); // possible hijacking
    return res.status(401).json({ error: 'Session invalid' });
  }
  res.json({ userId: req.session.userId });
});

// Logout
app.post('/logout', (req, res) => {
  req.session.destroy(() => res.clearCookie('connect.sid').json({ message: 'Logged out' }));
});

⚠️ Don’t Do This

❌ Using default MemoryStore in production

// MemoryStore leaks memory, doesn't scale, loses sessions on restart!
app.use(session({
  secret: 'keyboard cat',
  resave: true,  // Also bad!
  saveUninitialized: true,  // Creates empty sessions
}));

✅ Always use Redis/DB-backed store in production

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

Testing

Add these tests to verify your secure session management with redis implementation works correctly:

// __tests__/redis.test.ts
import { describe, it, expect } from 'vitest';

describe('Secure Session Management with Redis', () => {
  it('should handle successful authentication', async () => {
    // Test the happy path
    const result = await authenticate({ email: 'test@example.com', password: 'valid' });
    expect(result.user).toBeDefined();
    expect(result.error).toBeNull();
  });

  it('should reject invalid credentials', async () => {
    const result = await authenticate({ email: 'test@example.com', password: 'wrong' });
    expect(result.user).toBeNull();
    expect(result.error).toBeDefined();
  });

  it('should handle missing fields', async () => {
    const result = await authenticate({ email: '', password: '' });
    expect(result.error).toBeDefined();
  });
});

Verification

# Start Redis
redis-server

# Start your app
npm start

# Login and check Redis for the session key
redis-cli KEYS 'sess:*'
# Should show your session key with TTL

Related Specs

Advanced

Role-Based Access Control (RBAC)

Role/permission system with middleware, database schema, and route guards

Auth & Identity
Advanced

Add 2FA/TOTP to Any App

TOTP with otplib, QR code generation, and backup codes

Auth & Identity
Intermediate

JWT Authentication in Express.js

Access/refresh token pattern with middleware guards and secure cookie storage

Auth & Identity