TL;DR
This guide shows you how to secure your WebSocket connections using JWT (JSON Web Tokens). We’ll cover verifying the token on connection and handling invalid tokens. It assumes you already have a system for issuing JWTs.
Steps
- Server-Side Setup: Receive Token
- When a client connects to your WebSocket server, expect the JWT as part of the connection process. This is commonly done via a query parameter in the URL or within an initial handshake message.
- Example (Node.js with
wslibrary): - Token Verification
- Use a library appropriate for your server language to verify the JWT’s signature and claims (expiry, issuer etc.). Do not implement this yourself. Use well-maintained libraries like
jsonwebtokenin Node.js or similar in other languages. - Example (Node.js):
- Important: Replace
'your-secret-key'with your actual secret key used to sign the JWTs. Keep this key secure! - Handle Invalid Tokens
- If token verification fails (e.g., invalid signature, expired token), close the WebSocket connection immediately.
- Send an appropriate error message to the client indicating authentication failure. This helps with debugging and user experience.
- Example: See code snippet in Step 2.
- Authenticated Connection Handling
- If the token is valid, store the user information (from the decoded JWT) associated with that WebSocket connection. This allows you to identify and authorize actions performed by the client.
- Example: Store in a map/dictionary keyed by the WebSocket object.
- Message Handling & Authorization
- When the client sends a message, retrieve the associated user information from your storage (e.g.,
connectedUsersmap). - Check if the user has permission to perform the requested action based on their role or other claims in the JWT.
- Example:
- Connection Closure
- When the WebSocket connection closes, remove the associated user information from your storage. This prevents memory leaks and ensures that stale data isn’t used.
const ws = new WebSocket.Server({ port: 8080 });
ws.on('connection', ws => {
const urlParams = new URLSearchParams(ws.upgradeReq.url);
const token = urlParams.get('token');
// ... further processing
});
const jwt = require('jsonwebtoken');
ws.on('connection', ws => {
const urlParams = new URLSearchParams(ws.upgradeReq.url);
const token = urlParams.get('token');
jwt.verify(token, 'your-secret-key', (err, decoded) => {
if (err) {
// Token is invalid
console.error('Invalid token:', err.message);
ws.send(JSON.stringify({ type: 'auth_failed', message: 'Invalid token' }));
ws.terminate(); // Close the connection
return;
}
// Token is valid, decoded contains user information
console.log('Decoded token:', decoded);
// ... proceed with authenticated connection
});
});
const connectedUsers = new Map(); // ws object -> user data
ws.on('connection', ws => {
// ... token verification...
connectedUsers.set(ws, decoded);
});
ws.on('message', message => {
const userData = connectedUsers.get(ws);
if (!userData) {
// User is not authenticated (connection likely lost)
console.error('Unauthorized access');
return;
}
// Check user permissions based on userData...
});
ws.on('close', () => {
connectedUsers.delete(ws);
});
Security Considerations
- HTTPS: Always use HTTPS for your WebSocket connections to prevent man-in-the-middle attacks.
- Secret Key Security: Protect your JWT secret key carefully. Do not hardcode it directly into your code; store it securely in environment variables or a configuration file.
- Token Expiration: Set appropriate expiration times for your JWTs to limit the impact of compromised tokens.
- Refresh Tokens: Consider using refresh tokens to allow clients to obtain new JWTs without requiring them to re-authenticate frequently.

