Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

socket.io and express 4 sessions

I would like to access the express 4 session in my socket.io app. I'm kind of new with Node and I have some troubles implementing this functionality.

I found a npm module that allows access to the express 4 session : https://www.npmjs.org/package/session.socket.io-express4 or https://github.com/eiriklv/session.socket.io

If you look at my app.js code below, I'm doing something wrong in the session, sessionStore or cookieParser setup because I just can't get this module working.

// init modules
var express = require('express');
var helmet = require('helmet');
var fs = require('fs');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var memoryStore = session.MemoryStore;
var app = express();

// set variables
var options = {
  key: fs.readFileSync('./openssl_keys/server_key.pem'),
  cert: fs.readFileSync('./openssl_keys/server_cert.pem')
};
var cookieSecret = "secret phrase";
var sessionStore = new memoryStore();

app.set('env', process.env.NODE_ENV || 'development');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser(cookieSecret));
app.use(session({
    secret: cookieSecret,
    cookie: {httpOnly: true, secure: true},
    store: sessionStore
}));
app.use(function(req, res, next){
    res.locals.session = req.session;
    next();

});
app.use(express.static(path.join(__dirname, 'public')));

//routes
require('./routes/index')(app);
require('./routes/test')(app);


// starting http and https servers
var http = require('http').createServer(app).listen(8000, function(){
    console.log("http server listening on port 8000");
});
var https = require('https').createServer(options, app).listen(8080, function(){
    console.log("https server listening on port 8080"); 
});

// starting socket.io & session handler
var serverIO = require('socket.io').listen(https);
var SessionSockets  = require('session.socket.io-express4');
var io = new SessionSockets(serverIO, sessionStore, cookieParser);

io.on('connection', function(err, socket, session){
    if(err) throw err;
    console.log("connected");
    //console.log(session);
    socket.on('clientMessage', function(content) {
        console.log("received client message")
        console.log(content);
    });

});

module.exports = app;

I tried multiples possibilities like :

  • Disabling https server.
  • Setting up a cookieParser object with secret phrase (so it "actually" exports the secret phrase to io = new SessionSockets(serverIO, sessionStore, cookieParser);)
  • Using minimal cookie options.

Anyway I'm a bit lost with this, any suggestions/critics are welcome.


UPDATE

Ok so after numerous tries I think I could get it work!

The problem is with the cookieParser initialization which the correct way seems to be :

var cookieParser = require('cookie-parser');
app.use(cookieParser());
app.use(session({
    secret: "secret phrase",
    cookie: {httpOnly: true, secure: true},
    store: sessionStore
}));
var io = new SessionSockets(serverIO, sessionStore, cookieParser());

Notice that if I use var io = new SessionSockets(serverIO, sessionStore, cookieParser); (instead of cookieParser()) then it ain't working. That seems to be the problem.

If I use :

app.use(cookieParser("secret phrase"));
app.use(session({
    secret: "secret phrase",
    cookie: {httpOnly: true, secure: true},
    store: sessionStore
}));
var io = new SessionSockets(serverIO, sessionStore, cookieParser("secret phrase"));

then the module crashes with the following error message :

session.socket.io-express4/session.socket.io.js:41
ake.signedCookies[key] = handshake.signedCookies[key].match(/\:(.*)\./).pop();
                                                                        ^
TypeError: Cannot call method 'pop' of null

But if I use :

app.use(cookieParser("secret phrase"));
app.use(session({
    secret: "secret phrase",
    cookie: {httpOnly: true, secure: true},
    store: sessionStore
}));
var io = new SessionSockets(serverIO, sessionStore, cookieParser());

Then everything looks fine.

Now in the cookie-parser doc (https://github.com/expressjs/cookie-parser) it's saying you can pass a secret key to get the cookies signed. Which is something I'd like to have.

Could someone explain me the relation with the cookie-parser secret phrase and the session secret phrase ? Do they have to be the same/different ?

like image 551
Bobby Shark Avatar asked May 06 '14 11:05

Bobby Shark


People also ask

Can you use Socket.IO with Express?

Socket.IO can be used based on the Express server just as easily as it can run on a standard Node HTTP server. In this section, we will fire the Express server and ensure that it can talk to the client side via Socket.IO.

Which is better Socket.IO or WS?

Both WebSocket vs Socket.io are popular choices in the market; let us discuss some of the major Difference Between WebSocket vs Socket.io: It provides the Connection over TCP, while Socket.io is a library to abstract the WebSocket connections. WebSocket doesn't have fallback options, while Socket.io supports fallback.

What is the difference between Socket.IO and socket IO client?

socket-io. client is the code for the client-side implementation of socket.io. That code may be used either by a browser client or by a server process that is initiating a socket.io connection to some other server (thus playing the client-side role in a socket.io connection).


2 Answers

Here's my solution for the following environment:

  • express 4.2.0
  • socket.io 1.1.0
  • cookie-parser 1.0.1
  • cookie-session 1.0.2

Code:

var cookieParser = require('cookie-parser')();
var session = require('cookie-session')({ secret: 'secret' };

...

app.use(cookieParser);
app.use(session);

...

io.use(function(socket, next) {
    var req = socket.handshake;
    var res = {};
    cookieParser(req, res, function(err) {
        if (err) return next(err);
        session(req, res, next);
    });
});

Then you can access the session from the socket's handshake:

io.on('connection', function (socket) {
    console.log("Session: ", socket.handshake.session);
});

For people wondering how/why this works:

  • We send the handshake request through the cookie parser so that cookies are available
  • Then we send the handshake through session middleware as if its a normal request
  • The middleware attaches session to the request
  • We use handshake because for all intents and purposes, it is a normal request, and the parser and session middleware can deal with it properly. This is why you must access the session through the handshake
like image 192
Sean Adkinson Avatar answered Oct 02 '22 23:10

Sean Adkinson


With the new express-session middleware all you have to do is to add the IO middleware:

io.use(function(socket, next) {
  session(socket.handshake, {}, next);
});

A complete example would look like this:

var io = require('socket.io')(server);

var Session = require('express-session'),
    SessionStore = require('session-file-store')(Session);
    session = Session({
      store: new SessionStore({ path: './tmp/sessions' }),
      secret: 'pass',
      resave: true,
      saveUninitialized: true
    });

io.use(function(socket, next) {
  session(socket.handshake, {}, next);
});

io.on('connection', function(socket){
  console.log('a user connected');
  socket.emit('chat message', "UserID: " + socket.handshake.session.uid);
});

I created a super mini npm package socket.io-express-session which works as I explained above.

like image 27
xpepermint Avatar answered Oct 02 '22 23:10

xpepermint