5. Authentication
1. Authentication Types
1.1 Session-based Authentication with Express and Socket.IO
✅ Setup: Install dependencies
npm install express express-session socket.io
📄 server.js
const express = require('express');const session = require('express-session');const http = require('http');const socketIO = require('socket.io');
const app = express();const server = http.createServer(app);const io = socketIO(server);
// Session middlewareconst sessionMiddleware = session({ secret: 'my-secret', resave: false, saveUninitialized: true});
app.use(sessionMiddleware);
// Fake login routeapp.get('/login', (req, res) => { req.session.user = { id: 1, name: 'John' }; res.send('Logged in');});
// Share session with socket.ioio.use((socket, next) => { sessionMiddleware(socket.request, {}, next);});
io.on('connection', (socket) => { const session = socket.request.session;
if (session && session.user) { console.log(`User ${session.user.name} connected`); socket.emit('welcome', `Welcome ${session.user.name}`); } else { console.log('Unauthenticated connection attempt'); socket.disconnect(); }});
server.listen(3000, () => { console.log('Server running on http://localhost:3000');});
The session is shared with Socket.IO via
socket.request.session
.
1.2 Token-based Authentication with JWT
✅ Setup: Install dependencies
npm install express jsonwebtoken socket.io
📄 server.js
const express = require('express');const jwt = require('jsonwebtoken');const http = require('http');const socketIO = require('socket.io');
const app = express();const server = http.createServer(app);const io = socketIO(server);
const SECRET_KEY = 'your-secret-key';
// Fake login route to issue tokenapp.get('/login', (req, res) => { const user = { id: 1, name: 'John' }; const token = jwt.sign(user, SECRET_KEY, { expiresIn: '1h' }); res.json({ token });});
// Socket.IO authentication middlewareio.use((socket, next) => { const token = socket.handshake.auth.token;
if (!token) return next(new Error('Authentication error'));
jwt.verify(token, SECRET_KEY, (err, user) => { if (err) return next(new Error('Authentication error')); socket.user = user; // attach user to socket next(); });});
io.on('connection', (socket) => { console.log(`User ${socket.user.name} connected`); socket.emit('welcome', `Hello ${socket.user.name}`);});
server.listen(3000, () => { console.log('Server running on http://localhost:3000');});
🧠 Client Example for Token-based Auth:
const socket = io('http://localhost:3000', { auth: { token: 'your-jwt-token-here' }});
Summary
Feature | Session-based | Token-based (JWT) |
---|---|---|
Storage | Server-side (memory/DB) | Client-side (localStorage/cookie) |
Use case | Web apps with login sessions | APIs, mobile apps, stateless auth |
Socket.IO integration | socket.request.session | socket.handshake.auth.token |
2. Sharing Session?
2.1 🔍 Why share session with Socket.IO?
In session-based authentication, user data (e.g., login status) is stored server-side, usually via a session middleware like express-session
. When using Socket.IO, it does not automatically share sessions with Express. So if a user logs in via Express, Socket.IO won’t know who they are unless you explicitly share the session with it.
- Express sets the user session during login (
req.session.user = ...
). - Socket.IO runs in a separate context and doesn’t have access to that session by default.
- You need the session in
socket.request.session
to verify if the user is logged in when a websocket connection is made.
✅ Sharing session enables this:
io.use((socket, next) => { // Share the session set by Express sessionMiddleware(socket.request, {}, next);});
io.on('connection', (socket) => { if (socket.request.session.user) { // Now we know which user connected console.log(`User ${socket.request.session.user.name} connected`); } else { console.log('Unauthenticated user tried to connect'); socket.disconnect(); }});
2.2 When not to share session?
You may not want to share sessions with Socket.IO if you’re building a system that:
- Doesn’t use server-side sessions at all (e.g., uses stateless auth like JWT)
- Prefers horizontal scalability without shared session storage (sessions add complexity in scaling)
- Is an API-first or microservices app where each service independently validates users
- Wants to avoid memory leaks or excess memory usage from
express-session
’s in-memory store
✅ Example: Using JWT instead of sessions
Let’s look at an example where you purposely do NOT share the Express session with Socket.IO, and instead use JWT authentication (stateless and scalable):
server.js
const express = require('express');const jwt = require('jsonwebtoken');const http = require('http');const socketIO = require('socket.io');
const app = express();const server = http.createServer(app);const io = socketIO(server);
const SECRET = 'supersecret';
// Login endpoint issues tokenapp.get('/login', (req, res) => { const user = { id: 1, name: 'Alice' }; const token = jwt.sign(user, SECRET, { expiresIn: '1h' }); res.json({ token });});
// NO session middleware used here// Auth via JWT on websocket handshakeio.use((socket, next) => { const token = socket.handshake.auth.token; if (!token) return next(new Error('No token provided'));
try { const user = jwt.verify(token, SECRET); socket.user = user; next(); } catch (err) { return next(new Error('Invalid token')); }});
io.on('connection', (socket) => { console.log(`User ${socket.user.name} connected`); socket.emit('message', `Hello ${socket.user.name}`);});
server.listen(3000, () => { console.log('JWT-based server running on http://localhost:3000');});
client.js
example
const socket = io('http://localhost:3000', { auth: { token: 'your-jwt-token-here' }});
🔍 Summary: Why skip session sharing?
Use JWT Instead | Don’t Share Sessions |
---|---|
Stateless and scalable | No express-session |
Easier to work across services | No server memory used |
Better for APIs and mobile apps | No session syncing needed |
So, you don’t share session when you want stateless, scalable, modern authentication.