WebSocket sessions in NodeJS with Express

WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user’s browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.

However, keeping track of sessions in both HTTP and WebSocket channels can be quite tricky – setting custom headers is currently not possible in browser implementations. That said, standard authentication can be used, and cookies are also sent as part of the HTTP handshake.

Because of this, it’s actually quite easy to make use of the standard session handling in Express in your websocket setup. In a standard Express app, you’ll probably see something like this:

<span class="kr">const</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"express"</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">session</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"express-session"</span><span class="p">);</span>

<span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">session</span><span class="p">({</span>
    <span class="nx">secret</span><span class="o">:</span> <span class="s2">"session-secret"</span><span class="p">,</span>
    <span class="nx">rolling</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="nx">resave</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="nx">saveUninitialized</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">});</span>

<span class="c1">// ... routing, etc</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="nx">PORT</span><span class="p">);</span>

This becomes slightly different when we include websockets, but not much:

<span class="kr">const</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"express"</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">http</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"http"</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">session</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"express-session"</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"ws"</span><span class="p">);</span>

<span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">session</span><span class="p">({</span>
    <span class="nx">secret</span><span class="o">:</span> <span class="s2">"session-secret"</span><span class="p">,</span>
    <span class="nx">rolling</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="nx">resave</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="nx">saveUninitialized</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">});</span>

<span class="c1">// ... routing, etc</span>

<span class="kd">var</span> <span class="nx">server</span> <span class="o">=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">createServer</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="nx">server</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="nx">PORT</span><span class="p">);</span>

<span class="kd">var</span> <span class="nx">socketServer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ws</span><span class="p">.</span><span class="nx">Server</span><span class="p">({</span><span class="nx">server</span><span class="o">:</span> <span class="nx">server</span><span class="p">});</span>
<span class="nx">socketServer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">"connection"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">connection</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// ... deal with connection</span>
<span class="p">});</span>

I’m using ws here, but there are a whole bunch of options.

Now, I’ll warn you, this is a bit of a hack, but it seems to do the trick. In order to share the session handling, we just wrap the connection process in the same handler that we passed in to our express app:

<span class="kd">var</span> <span class="nx">sessionHandler</span> <span class="o">=</span> <span class="nx">session</span><span class="p">({</span>
    <span class="nx">secret</span><span class="o">:</span> <span class="s2">"session-secret"</span><span class="p">,</span>
    <span class="nx">rolling</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="nx">resave</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="nx">saveUninitialized</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">});</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">sessionHandler</span><span class="p">);</span>

<span class="c1">// ...</span>

<span class="nx">socketServer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">"connection"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">connection</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">request</span> <span class="o">=</span> <span class="nx">connection</span><span class="p">.</span><span class="nx">upgradeReq</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">response</span> <span class="o">=</span> <span class="p">{</span><span class="nx">writeHead</span><span class="o">:</span> <span class="p">{}};</span> <span class="c1">// Particularly nasty</span>

    <span class="nx">sessionHandler</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
        <span class="cm">/**</span>
<span class="cm">         * At this point, `request.session` is set up, and you</span>
<span class="cm">         * can use it as you would in a regular route.</span>
<span class="cm">         */</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">request</span><span class="p">.</span><span class="nx">session</span><span class="p">.</span><span class="nx">user</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">connection</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s2">"Invalid session"</span><span class="p">);</span>
            <span class="nx">connection</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// ... and so on.</span>
    <span class="p">});</span>
<span class="p">});</span>

This is of course a hack, and I have no idea if it would work as is with any other sockets library. But it does the trick for now.

Comments

Prashant Jain

you are so f'kin awesome. you solved one of my big problem. Thanks. Thanks. Thanks. Thanks.

Rodolphe

i've passed 3 days looking for one solution gives me how to share sessions in express with ws. lots of thanks for your article. i'm very happy , thanks again!

Your email address will not be published. Required fields are marked *

HTML is not allowed (it'll appear as typed), but you can use Markdown instead. For the unitiated, paragraphs will be auto-inserted, but links won't be converted unless you [write them properly](http://example.com/).