alexroat
alexroat

Reputation: 1727

Node.js, ws and express-session : how to get session object from ws.upgradeReq

I have an application built on express, express-session and ws ( for the websocket support).

I have used this code to enable the session middleware:

app.use(session({secret: 'mysecret',resave: true,saveUninitialized: true,cookie: {}}));

And I can read and save session variables within a get handler with:

app.get('/', function (req, res) {
  res.send('Hello World!');
  console.log(req.session.test);
  req.session.test="OK";
  req.session.save();
});

(the first time it prints "undefined" and the second time it prints "OK" as expected)

Now I have a websocket server that is handled by express too in the same app and I would like to get the req.session object inside the ws.on("connection",...) and in ws.on("message",...).

The problem is that there is no reference to the original req object, just a reference to a upgradeReq that is a wrapper of the connection handler of the websocket. Inspecting this upgradeReq I can find the connect.sid in ws.upgradeReq.headers.cookie that contains the session cookie value.

wss.on('connection', function connection(ws) {
  var location = url.parse(ws.upgradeReq.url, true);
  console.log(ws.upgradeReq.headers.cookie);
  //....
});

In my idea could be possible to get the original req.session from this in some way.

How can I accomplish this ?

Upvotes: 9

Views: 9262

Answers (2)

dritan
dritan

Reputation: 902

I just found out that at version 3.0.0 upgradeReq was removed from WebSocket. A workaround for existing code of alexroat:

wss.on('connection', function connection(ws, req) {
    ws.upgradeReq = req;
    var location = url.parse(ws.upgradeReq.url, true);
    //get sessionID
    var cookies = cookie.parse(ws.upgradeReq.headers.cookie);
    var sid = cookieParser.signedCookie(cookies["connect.sid"], secret);
    //get the session object
    store.get(sid, function (err, ss) {
        //create the session object and append on upgradeReq
        store.createSession(ws.upgradeReq, ss)
        //setup websocket bindings
        ws.on('message', function incoming(message) {
            console.log('received: %s', message);
            //..........
        });
    });
});

Upvotes: 8

alexroat
alexroat

Reputation: 1727

Ok, I've found an undocumented way studying the source code of express-session.

First of all you need cookie and cookie-parser as your dependency.

so, import the modules

var cookie = require('cookie');
var cookieParser = require('cookie-parser')

Then, the trick is to decode the signed cookie in the ws "connection" event handler that is when there is the websocket handshake. First of all you need to decode from the cookie the connect.sid that is the session id (only if you are using express-session for the session management)

wss.on('connection', function connection(ws) {
    var cookies=cookie.parse(ws.upgradeReq.headers.cookie);
    var sid=cookieParser.signedCookie(cookies["connect.sid"],secret);
    ...

TAke care that the secret is the same that you have set for the signature in express-session (that is in app.use(session({secret: secret, resave: true, saveUninitialized: true, cookie: {},store:store})); ) At this point you have the sessoinID in sid, the you want to get the Session Object from express-session, that is passed you asyncronously from the store ( there are many, read the doc, at the moment I've tested only with the MemoryStore of express-session).

In order to get the session then use this code, right after the lines above

...
store.get(sid,function(err, ss){
        store.createSession(ws.upgradeReq,ss)
    });

the callback function will give you the session object (ss) directly from the store but it is alone and without methods ( is just a deserialized JSON). So for this reason we use again the store.createSession to which we pass the upgradeReq and the ss object, in this way I have a session object that has all the documented methods ( so I can also modify the session from ws callbacks).

The new session object is available in:

ws.upgradeReq.session

Take care: The session object is not available until the store asyncronously return the ss object, so it would be a good practise to complete the websocket event bindings ( like on('message',...)inside the callback passed to the store.get.

This is the complete code:

wss.on('connection', function connection(ws) {
    var location = url.parse(ws.upgradeReq.url, true);
    //get sessionID
    var cookies = cookie.parse(ws.upgradeReq.headers.cookie);
    var sid = cookieParser.signedCookie(cookies["connect.sid"], secret);
    //get the session object
    store.get(sid, function (err, ss) {
        //create the session object and append on upgradeReq
        store.createSession(ws.upgradeReq, ss)
        //setup websocket bindings
        ws.on('message', function incoming(message) {
            console.log('received: %s', message);
            //..........
        });

    });
});

SO, now you can manage session data using ws and express-session together.

I hope it will help!

Upvotes: 10

Related Questions