How to authenticate WebSocket connections
JWT in query string, Authorization header on upgrade, cookie-based auth, verifying token on connection, rejecting unauthenticated clients, per-client auth state, token refresh with WebSocket
Authenticate at Connection Time
WebSocket upgrades are regular HTTP requests. Authenticate before accepting the connection. The cleanest approach for browser clients is a JWT in the query string (not ideal for sensitive tokens, but practical):
const jwt = require('jsonwebtoken'); const SECRET = process.env.JWT_SECRET; wss.on('connection', (ws, req) => { const url = new URL(req.url, 'http://localhost'); const token = url.searchParams.get('token'); let user; try { user = jwt.verify(token, SECRET); } catch (err) { ws.close(4001, 'Invalid token'); return; } ws.userId = user.id; ws.send(JSON.stringify({ type: 'authenticated', userId: user.id })); });
Using the HTTP Server for Pre-Auth
A more secure approach: authenticate with a REST endpoint first, receive a short-lived one-time token (OTT), pass the OTT on the WebSocket URL. The server validates and immediately marks it used. This avoids long-lived tokens in URLs appearing in server logs. Reject any connection where token validation fails — close with code 4001 immediately, before any application data is sent.
