Skip to content

4. Session Handling

1. What is socket.handshake in Socket.IO?

  • It’s an object containing info about the initial HTTP/WebSocket connection request.

  • Represents the handshake phase where the client and server upgrade from HTTP to WebSocket.

  • Includes details like:

    • headers — HTTP headers sent by the client
    • query — URL query parameters from client connection URL
    • auth — authentication data sent during connection (like tokens)
    • time — timestamp of the handshake
    • address — client’s IP address

A WebSocket connection starts with an HTTP GET request like this:

GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Authorization: Bearer <token> <-- custom header (optional)
Cookie: sessionId=abc123 <-- or cookie (optional)

The server can inspect headers and either accept or reject the connection.

1.1 Why is it called “handshake”?

Socket.IO uses a HTTP polling handshake first, then upgrades to WebSocket (or directly WebSocket if possible).

This is a higher-level handshake on top of the underlying TCP connection, which establishes the WebSocket session and exchanges metadata before real-time communication starts.

TCP handshake vs Socket.IO handshake:

AspectTCP Handshakesocket.handshake in Socket.IO
LevelTransport Layer (TCP protocol)Application Layer (Socket.IO/WebSocket)
What it doesEstablishes a TCP connection (SYN, SYN-ACK, ACK)Contains info about the HTTP/WebSocket upgrade request
When it happensBefore any data transferDuring the initial Socket.IO connection setup
PurposeLow-level connection establishmentExchanging metadata & authenticating clients

1.2 Summary

  • socket.handshake is metadata about the client’s connection request.
  • It’s not the TCP handshake, but a logical handshake at the Socket.IO layer.
  • Useful for extracting headers, auth info, cookies, etc., at the time the socket connects.

2. Session Handling

2.1 Session handling in WebSockets is tricky

  • Express sessions rely on HTTP cookies.
  • WebSocket connections start with an HTTP handshake, then upgrade.
  • After upgrade, no HTTP headers are sent, so no new cookies.
  • You must extract the session cookie during the handshake and load the session manually.

2.2 Share Express sessions with Socket.IO

To share Express sessions with WebSockets:

  1. Set up express-session.
  2. Use the same session middleware with Socket.IO’s handshake.
  3. Access session data on socket connections.

1. Setup Express and express-session

const express = require('express');
const session = require('express-session');
const app = express();
const sessionMiddleware = session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: { secure: false } // set true if using HTTPS
});
app.use(sessionMiddleware);

2. Setup Socket.IO and share session middleware

const http = require('http');
const { Server } = require('socket.io');
const server = http.createServer(app);
const io = new Server(server);
// Share session middleware with Socket.IO
io.use((socket, next) => {
sessionMiddleware(socket.request, {}, next);
// Here you are attaching the session to the socket.request during the WebSocket handshake.
// This makes the session data available at socket.request.session.
// Sessions are only available through socket.request.session once — during the initial connection (WebSocket handshake).
//
// After the handshake, regular WebSocket messages (e.g., socket.on(...)) don’t go through middleware again,
// but socket.request.session still holds the session data, including any mutations you make to it.
//
// This means:
// You can read and modify the session during the connection lifecycle.
// But session persistence (e.g. saving updated values) needs a manual call to session.save().
});

3. Access session data inside socket connection

io.on('connection', (socket) => {
const session = socket.request.session;
console.log('Session ID:', session.id);
// You can read/write session properties here
if (!session.views) {
session.views = 0;
}
session.views++;
session.save(); // Refer to the notes below
socket.emit('sessionData', { views: session.views });
});
Why in non-websockets context, session.save() is not required?

In non-WebSocket (i.e. normal HTTP) contexts, like handling a request in Express:

app.get('/some-route', (req, res) => {
req.session.counter = (req.session.counter || 0) + 1;
res.send(`Counter: ${req.session.counter}`);
});

You usually don’t need to call req.session.save() manually, because:

✅ Why session.save() is not needed in HTTP
  1. Automatic save at the end of the request
  • express-session automatically detects if the session was modified, and saves it before the response ends.
  • It hooks into res.end() or res.send() internally.
  1. Middleware lifecycle handles persistence
  • When the HTTP request completes, Express finalizes the response, and express-session saves the session data to the store (memory, Redis, etc.).
  • This happens behind the scenes — no manual save needed.
❌ Why this doesn’t work in WebSockets:
  • WebSocket events (like socket.on('event', ...)) are not part of the Express request-response lifecycle.
  • There’s no res.end() for WebSocket events — so express-session has no signal to save the session automatically.
  • Therefore, you must call socket.request.session.save() manually if you modify the session inside a WebSocket event.
🧠 Summary
Contextsession.save() needed?Why?
HTTP❌ Not neededAuto-saved at end of request
WebSocket✅ Required (if modified)No auto-save trigger — must call manually

4. Example route that initializes session data

app.get('/', (req, res) => {
if (!req.session.username) {
req.session.username = 'Nadith';
}
res.sendFile(__dirname + '/index.html');
});

5. [OPTIONAL] Express + Socket.IO with Redis session store

const RedisStore = require('connect-redis')(session);
const redisClient = require('redis').createClient();
const sessionMiddleware = session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false
});

Summary

StepDescription
Use express-sessionSetup session middleware in Express
Share session with Socket.IOUse io.use() to run session middleware on socket requests
Access session dataRead/write via socket.request.session
Save changesCall session.save() if you modify session

3. Cookies and Web Sockets

WebSocket-based connections, the HTTP cookies (including connect.sid) are only sent during the initial handshake, not after.

So how does the server identify the session in subsequent WebSocket messages?

✅ How It Works in Practice (Socket.IO + Express Sessions)

🔁 1. During Handshake:
  • The browser sends cookies: connect.sid=abc123

  • Socket.IO middleware runs:

    sessionMiddleware(socket.request, {}, next);
  • This sets socket.request.session with the session data

  • You can now check socket.request.session.userId or anything you stored

💾 2. Persist User Info in Socket

Once authenticated, store relevant session data on the socket:

io.on('connection', (socket) => {
const session = socket.request.session;
socket.userId = session.userId;
socket.on('message', (msg) => {
console.log(`[${socket.userId}] sent: ${msg}`);
});
});

Now you don’t need to read cookies anymore — you’re using socket.userId.

3. 🔄 What About Reconnects?

When the client disconnects and reconnects:

  • A new handshake is made
  • Cookies are sent again
  • The session middleware runs again
  • session.userId is re-attached

So the session still works seamlessly.

4. 🛡️ Summary

StepMechanism
Initial handshakeCookies are sent (e.g. connect.sid)
Session loadedVia express-session middleware
User info storedAttached to socket object
Persistent stateSession lives in memory for connection
ReconnectsCookies sent again — session restored

4. Socket.IO Middleware

Socket.IO has its own middleware system**, separate from Express.

4.1 🔧 WebSocket (Socket.IO) Middleware

Socket.IO supports connection-level middleware:

io.use((socket, next) => {
// This runs during the handshake (one time per connection)
const token = socket.handshake.auth.token;
if (!token) return next(new Error('Unauthorized'));
// Attach user info, etc.
next();
});

And you can do application logic in event handlers like:

io.on('connection', (socket) => {
socket.on('chat', (msg) => {
// Handle real-time message
});
});

4.2 🔁 Express Lifecycle vs. Socket.IO Lifecycle

FeatureHTTP (Express)WebSocket (Socket.IO)
Session auto-save✅ Yes❌ No (session.save() required)
Uses req, res, next()✅ Yes❌ Only during handshake via socket.request
Middleware runs per request✅ Yes❌ Runs only once per connection
Custom event-based logic❌ Not native✅ Yes (socket.on(...))

✅ Conclusion

  • WebSocket events are not in the Express lifecycle, but Socket.IO provides its own middleware, specifically for:

    • Authentication (during connection)
    • Custom logic (inside socket.on())

If you need per-event middleware, you’d have to write your own wrappers or handlers — Socket.IO doesn’t have built-in per-event middleware like Express has per-route.