T J
T J

Reputation: 43156

express-session isn't setting session cookie while using with socket.io

I'm trying to implement authentication and sessions with socket.io.

After a lot of research 1, I've set up the following which makes use of express and express-session.

The issue is that, express and socket.io connections seems to be having different sessions since the id I'm getting is different.

Also, no cookie is set in the browser from my application (which is possibly why both components are creating different sessions?)

I'm using express 4.13.3 express-session 1.12.1 and socket.io 1.3.7.

Below is the server side code that I've set up:

var app = require('express')();
var server = app.listen(80);
var parser = require('body-parser');
var Session = require('express-session');
var io = require('socket.io')(server);
var game = require('./game.js');
var session = Session({
  secret: 'some secret',
  resave: false,
  saveUninitialized: false,
  cookie: {
    'name': 'test',
    httpOnly: false,
    secure: false,
    maxAge: ((60 * 1000) * 60)
  }
});
app.use(parser.urlencoded({
  extended: false
}));
app.use(session);
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});
app.post('/login', function(req, res) {
  console.log('login %s', req.session.id);
  console.dir(req.session, {
    color: true
  });
  res.json({
    status: 'success'
  });
});
io.use(function(socket, next) {
  session(socket.handshake, {}, next);
});
console.log('socket server started @ %s !', 80);
io.on('connection', function(socket) {
  console.log('user connected %s', socket.handshake.session.id);
  console.dir(socket.handshake.session, {
    color: true
  });
  socket.on('disconnect', function() {
    console.log('user disconnected');
    console.dir(socket.handshake.session, {
      color: true
    });
  });
});

and the code at client for testing purpose:

$.ajax({
   url: 'http://127.0.0.1:80/login',
   type:'post',
   success: function(response) {
      var socket = io('http://127.0.0.1:80');
   }
});

Below is the output in console:

enter image description here

and this is the resources section in browser:

enter image description here

I'm even confused seeing that the session object is not having any id property when logged in console, but session.id prints different values. Maybe this is coming from the objects prototype chain or something.

The only difference I can think of with my code and the tutorials out there is that, they are all serving the index.html page from the same express server. In my case I've the file locally.

I've read somewhere that in such cases I must send an http request to express before establishing the socket.io connection.

The socket.io.js client side library is served from the same server which is shared by express and socket.io (<script src="http://127.0.0.1/socket.io/socket.io.js"></script>), apart from this I'm also hitting the express route /login for testing purpose before the socket connection is established.

So I'm wondering why no cookie is being set in client side, and Why the express and socket requests have created different sessions..? What am I missing here..?


1 The code is mostly based on:


P.S: What I'm trying to do is, when a socket client re-connects, I need to put him back to where he was with the session details. I don't really want to share sessions with express, but express middleware is the only way I've came across to implement sessions with socket.io.

Upvotes: 3

Views: 6917

Answers (2)

T J
T J

Reputation: 43156

The problem had nothing to do with express or socket.io. The issue is that, I was loading the project from a local server other than the express and socket.io server.

So it's a cross domain ajax request, in which case some browsers refuse to handle the Set-Cookie header. We can fix this by adding the following in ajax options as mentioned in this answer:

xhrFields: {
   withCredentials: true
},

Or if you're working with angular, sent {withCredentials: true} like

$http({withCredentials: true, ...}).get(...) as mentioned in this answer.

At server side, set this header:

res.header("Access-Control-Allow-Credentials", 'true');

If you add that, chrome will not allow you to use wildcard * in the Access-Control-Allow-Origin header anymore. You should also set it to specific allowed domains.

res.header("Access-Control-Allow-Origin", "allowed domains");

Upvotes: 7

kaytrance
kaytrance

Reputation: 2757

Shouldn't you

res.session.save(); // add this line
res.json({ status: 'success' }); // or just res.send({status:success});

?

And also, I kind of recommend you to use passport module with passport-local strategy for this purposes so you do not have to bother why your sessions do not work. Once it saved me a lot of time.

Upvotes: -1

Related Questions