Skip to content

WebSocket Middleware for Authentication — Complete Guide

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Websocket Middleware for Authentication. We cover key concepts, practical examples, and best practices to help you master this topic.

WebSocket middleware functions execute during the connection lifecycle. They can validate authentication tokens, enforce rate limits, log connections, and transform messages. Socket.IO provides built-in middleware support for namespaces and sockets.

What You'll Learn

  • Socket.IO middleware for namespaces and sockets
  • ws library middleware patterns
  • Token authentication during handshake
  • Rate Limiting per connection
  • Message validation and transformation

Why It Matters

Middleware separates cross-cutting concerns from business logic. Authentication, logging, and rate limiting applied as middleware are consistent across all connection types.

Real-World Use

Discord authenticates WebSocket connections during the handshake. Slack uses middleware for rate limiting per user. Many real-time APIs validate message schemas in middleware before routing to handlers.

flowchart LR
    Request[Connection Request] --> Auth[Auth Middleware]
    Auth -->|Pass| RateLimit[Rate Limit Middleware]
    Auth -->|Fail| Reject[Reject Connection]
    RateLimit -->|OK| Log[Logging Middleware]
    RateLimit -->|Exceeded| Reject
    Log --> Handler[Connection Handler]
    Handler --> Message[Message Handler]
    Message --> Validate[Validation Middleware]
    Validate --> Process[Process Message]

Teacher Mindset

Apply authentication middleware at the namespace level for consistency. Use socket-level middleware for per-message validation. Keep middleware pure and stateless for testability.

Code Examples

// Example 1: Socket.IO namespace middleware
const io = require('socket.io')(3000);

const chat = io.of('/chat');

// Auth middleware for the chat namespace
chat.use((socket, next) => {
  const token = socket.handshake.auth.token;
  const userId = socket.handshake.auth.userId;

  if (!token) {
    return next(new Error('Authentication required'));
  }

  try {
    const user = jwt.verify(token, process.env.JWT_SECRET);
    socket.user = user;
    socket.userId = userId;
    next();
  } catch (err) {
    next(new Error('Invalid token'));
  }
});

chat.on('connection', (socket) => {
  console.log('Authenticated user:', socket.user.name);
});
// Example 2: Message validation middleware
const validateMessage = (socket, next) => {
  socket.use((packet, next) => {
    const [event, data] = packet;

    if (event === 'chat message') {
      if (!data.text || typeof data.text !== 'string') {
        return next(new Error('Invalid message format'));
      }
      if (data.text.length > 1000) {
        return next(new Error('Message too long'));
      }
      data.text = data.text.trim();
    }

    next();
  });
};

io.use(validateMessage);

io.on('connection', (socket) => {
  socket.on('chat message', (data) => {
    io.emit('chat message', data);
  });
});
// Example 3: Rate limiting middleware
const rateLimit = new Map();

const rateLimitMiddleware = (socket, next) => {
  const userId = socket.userId || socket.handshake.address;
  const now = Date.now();

  if (!rateLimit.has(userId)) {
    rateLimit.set(userId, []);
  }

  const timestamps = rateLimit.get(userId);
  const windowStart = now - 10000; // 10-second window

  // Remove old timestamps
  const recent = timestamps.filter(t => t > windowStart);
  recent.push(now);
  rateLimit.set(userId, recent);

  if (recent.length > 10) { // 10 messages per 10 seconds
    return next(new Error('Rate limit exceeded'));
  }

  next();
};

io.use(rateLimitMiddleware);

Common Mistakes

  • Putting authentication logic in connection handlers instead of middleware
  • Not cleaning up rate limit state when connections close
  • Throwing errors in middleware instead of calling next(new Error())
  • Making middleware stateful across connections
  • Forgetting that Socket.IO middleware runs before the connection event

Practice

  1. Implement JWT authentication middleware for a Socket.IO namespace.
  2. Add message validation middleware that checks message size and format.
  3. Implement rate limiting middleware per user ID.
  4. Add logging middleware that records connection time and duration.
  5. Challenge: Build a middleware chain that supports three-tier rate limiting (per user, per IP, global).

FAQ

When does Socket.IO middleware execute?

Middleware executes during the connection handshake, before the connection event fires.

Can I have multiple middleware functions?

Yes. Middleware functions execute in order. Pass to next() to continue the chain.

How do I pass data from middleware to handlers?

Attach properties to the socket object (socket.user). This data is available in all handlers.

Does the ws library support middleware?

The ws library does not have built-in middleware. Implement middleware by wrapping event handlers.

How do I handle middleware errors gracefully?

Send the error to the client via an error event or close the connection with a meaningful status code.

Mini Project

Build a WebSocket server with three middleware layers: authentication (JWT), rate limiting (10 messages per 10 seconds), and message validation (max 1000 characters). Return appropriate errors for each failure case.

What's Next

Next, you will learn about WebSocket reconnection strategies for resilient connections.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro