I recently spent some time adding mutual TLS (mTLS) support to a Node.js Express app. The app was using a library called express-ws to handle WebSocket connections.
Problem
There are hundreds of examples out there of how to set up an Express app with a certificate and key for TLS. And there are dozens of other examples of how to set up a TLS-enabled WebSocket server, using a variety of different libraries. But it took several iterations before I got things just right for the specific combination of Express/express-ws/mTLS.
I did eventually figure it out. And, of course, after getting it working, I found an example in the express-ws repository that lays out exactly what I needed to do.
But if anyone else finds themselves working with this same combination of technologies, I hope this post can save them time and searching.
Solution
Here’s what I ended up with:
const fs = require("fs");
const https = require("https");
const express = require('express');
const expressWs = require('express-ws');
const options = {
key: fs.readFileSync(`${__dirname}/certs/private.key`),
cert: fs.readFileSync(`${__dirname}/certs/server-cert.pem`),
ca: [
fs.readFileSync(`${__dirname}/certs/ca-root-cert.pem`),
],
requestCert: true,
rejectUnauthorized: true,
};
const app = express();
const server = https.createServer(options, app);
// Apply the express-ws support, passing the https server as well
expressWs(app, server);
app.get('/', (req, res) => {
res.send(JSON.stringify({hello: "world"}));
res.end();
});
app.ws('/', (ws) => {
console.log("Got websocket connection")
ws.on("message", async (message) => {
const data = JSON.parse(message);
console.log("Received message", data)
ws.send(JSON.stringify({ body: "foo" }));
});
});
server.listen(3000)
console.log("Express HTTP and Websocket server listening on 3000");
To summarize the code, the right order of operations ended up being:
- Create the Express app.
- Create the HTTPS server, providing the TLS options and the Express app.
- Pass both of those things to the
expressWs
function. - Add the route handlers.
- Call listen on the HTTPS server object.
The express-ws
library makes it easy to add WebSocket support seamlessly to an Express application. It’s often a little tricky to get things set up for HTTPS and WSS, so I hope this was helpful!