WebSocket connections require robust authentication to ensure only authorized clients participate in real-time communication. Several practical approaches can be applied depending on security needs and system architecutre.
Token-Based Atuhentication
Clients include a token during the initial handshake, typically via the Sec-WebSocket-Protocol header. The server validates this token before accepting the connection.
const ws = require('ws');
const wss = new ws.Server({ port: 3000 });
wss.on('connection', (clientSocket, request) => {
const authHeader = request.headers['sec-websocket-protocol'];
if (!verifyToken(authHeader)) {
clientSocket.close(4001, 'Invalid token');
return;
}
clientSocket.send('Authenticated successfully');
});
function verifyToken(token) {
// Implement JWT or custom token validation
return token === 'Bearer valid_token_123';
}
Client-side usage:
const conn = new WebSocket('ws://localhost:3000', 'Bearer valid_token_123');
Signature-Based Verification
A cryptographic signature is genearted by the client using a shared secret and request-specific data (e.g., URL and WebSocket key). The server recomputes the signature to verify authenticity.
const crypto = require('crypto');
const SECRET = 'shared_secret_key';
wss.on('connection', (socket, req) => {
const providedSig = req.headers['x-signature'];
const payload = req.url + req.headers['sec-websocket-key'];
const expectedSig = crypto
.createHmac('sha256', SECRET)
.update(payload)
.digest('hex');
if (providedSig !== expectedSig) {
socket.close(4002, 'Invalid signature');
return;
}
socket.send('Signature verified');
});
IP Whitelisting
Restrict connections to known IP addresses, useful for internal services or machine-to-machine communication.
const ALLOWED_IPS = new Set(['::1', '127.0.0.1', '192.168.1.10']);
wss.on('connection', (socket, req) => {
const ip = req.socket.remoteAddress;
if (!ALLOWED_IPS.has(ip)) {
socket.close(4003, 'IP not allowed');
return;
}
socket.send('IP authorized');
});
OAuth 2.0 Integration
Leverage existing identity providers by validating access tokens against an introspection endpoint.
const axios = require('axios');
wss.on('connection', async (socket, req) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
socket.close(4004, 'Missing or invalid auth header');
return;
}
const token = authHeader.substring(7);
try {
const res = await axios.post('https://auth.example.com/introspect',
new URLSearchParams({ token }),
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
);
if (!res.data.active) throw new Error('Token inactive');
socket.send('OAuth token valid');
} catch (err) {
socket.close(4005, 'OAuth verification failed');
}
});
Best Practices
- Prefer short-lived tokens with proper revocation mechanisms.
- Avoid embedding sensitive data in URLs or headers that may be logged.
- Always close unauthorized connections immediately with appropriate close codes.
- Use HTTPS/WSS in production to prevent token interception.
- Validate and sanitize all input even after authentication.
These strategies can be combined—for instance, requiring both a valid OAuth token and source IP restriction for high-security applications.