55

I want to create a user sessison when user enter the app. And read the session whenever needed. Here is my try

var io   = require('socket.io'),
    express = require('express');
    querystring = require('querystring');

var app = express.createServer();
app.get('/', function(req, res){
    var sessionVal = querystring.parse(req.url.substr(2));// sessionVal is an email for example: me@gmail.com
    app.use(express.cookieParser());
    app.use(express.session({ secret: sessionVal }));
});
var socket = io.listen(app);
socket.on('connection', function(client) {
    client.on('message', function(message) {
        // message will be an object {text:'user text chat blah blah', email:'me@gmail.com'}
        // if the seesion stored, has the same value with message.email
        // then the message will be broadcasted
            socket.broadcast(message.text);
        // else will not broadcast  
    });
});

app.listen(4000);
angry kiwi
  • 10,730
  • 26
  • 115
  • 161

5 Answers5

80

I need to point out here that you're incorrectly adding middleware to the application. The app.use calls should not be done within the app.get request handler, but outside of it. Simply call them directly after createServer, or take a look at the other examples in the docs.

The secret you pass to express.session should be a string constant, or perhaps something taken from a configuration file. Don't feed it something the client might know, that's actually dangerous. It's a secret only the server should know about.

If you want to store the email address in the session, simply do something along the lines of:

req.session.email = req.param('email');

With that out of the way...


If I understand correctly, what you're trying to do is handle one or more HTTP requests and keep track of a session, then later on open a Socket.IO connection from which you need the session data as well.

What's tricky about this problem is that Socket.IO's means of making the magic work on any http.Server is by hijacking the request event. Thus, Express' (or rather Connect's) session middleware is never called on the Socket.IO connection.

I believe you can make this work, though, with some trickery.

You can get to Connect's session data; you simply need to get a reference to the session store. The easiest way to do that is to create the store yourself before calling express.session:

// A MemoryStore is the default, but you probably want something
// more robust for production use.
var store = new express.session.MemoryStore;
app.use(express.session({ secret: 'whatever', store: store }));

Every session store has a get(sid, callback) method. The sid parameter, or session ID, is stored in a cookie on the client. The default name of that cookie is connect.sid. (But you can give it any name by specifying a key option in your express.session call.)

Then, you need to access that cookie on the Socket.IO connection. Unfortunately, Socket.IO doesn't seem to give you access to the http.ServerRequest. A simple work around would be to fetch the cookie in the browser, and send it over the Socket.IO connection.

Code on the server would then look something like the following:

var io      = require('socket.io'),
    express = require('express');

var app    = express.createServer(),
    socket = io.listen(app),
    store  = new express.session.MemoryStore;
app.use(express.cookieParser());
app.use(express.session({ secret: 'something', store: store }));

app.get('/', function(req, res) {
  var old = req.session.email;
  req.session.email = req.param('email');

  res.header('Content-Type', 'text/plain');
  res.send("Email was '" + old + "', now is '" + req.session.email + "'.");
});

socket.on('connection', function(client) {
  // We declare that the first message contains the SID.
  // This is where we handle the first message.
  client.once('message', function(sid) {
    store.get(sid, function(err, session) {
      if (err || !session) {
        // Do some error handling, bail.
        return;
      }

      // Any messages following are your chat messages.
      client.on('message', function(message) {
        if (message.email === session.email) {
          socket.broadcast(message.text);
        }
      });
    });
  });
});

app.listen(4000);

This assumes you only want to read an existing session. You cannot actually create or delete sessions, because Socket.IO connections may not have a HTTP response to send the Set-Cookie header in (think WebSockets).

If you want to edit sessions, that may work with some session stores. A CookieStore wouldn't work for example, because it also needs to send a Set-Cookie header, which it can't. But for other stores, you could try calling the set(sid, data, callback) method and see what happens.

mmm
  • 2,272
  • 3
  • 25
  • 41
Stéphan Kochen
  • 19,513
  • 9
  • 61
  • 50
  • thanks for every detail explaination, I use req.session.email = req.param('email'); How can I access the email value stored ? – angry kiwi Apr 24 '11 at 06:56
  • I forgot to tell a bug when i use I use req.session.email = req.param('email'), the server error says cannot sett property email of undefined. – angry kiwi Apr 24 '11 at 09:22
  • I still believe you're using middleware wrong. When done properly, you should have a `req.session`. As I've shown in the example, you can access the stored email using `req.session.email`. – Stéphan Kochen Apr 24 '11 at 09:50
  • I've added a more complete example. This code runs, and the session related code for the `GET /` handler works for me. I didn't actually test the socket code, but believe it should work. – Stéphan Kochen Apr 24 '11 at 10:04
  • @Shtééf Can we get the session by it's value instad of it's id ? – angry kiwi Apr 24 '11 at 12:06
  • The problem is that the browswer doesn't have a cookie connect.id. Therefore there is no session id to send to nodejs server. Express couldn't generate a cookie maybe because domains conflict? My node server is localhost and the client from is www.mysite.com/chat.php ? – angry kiwi Apr 24 '11 at 12:10
  • 2
    It looks like the cookie is set to HttpOnly by default. `express.session` also takes a `cookie` option. Take a look at: http://senchalabs.github.com/connect/middleware-session.html – Stéphan Kochen Apr 24 '11 at 12:45
  • I tried change the httponly to false, I even tried res.header('Access-Control-Allow-Origin', '*'); but the cookie just won't appear in browser, eventhough, the in firebug console, there indeed a header cookie. But I was able to use a work around to create the cookie in the browser, I use this res.send({sid:req.session.id,email: req.session.email}); that will send the created session id to the brower, then I was able to use jquery cookie to create a cookie with that value. – angry kiwi Apr 24 '11 at 13:31
  • Now everything looks good, it amazing how you understand the issues thoroughly. Now, The sessions in the nodejs server will be auto destroyed or I have to do it manually ? – angry kiwi Apr 24 '11 at 13:35
  • It's usually the session store responsibility to clean up sessions. But the MemoryStore is mostly for development, it doesn't do any clean up and stores them forever. You can see here that the Redis store adds a TTL (Time-To-Live) to the record: https://github.com/visionmedia/connect-redis/blob/master/lib/connect-redis.js#L79 – Stéphan Kochen Apr 24 '11 at 13:48
  • @cathy.sasaki, please do not attempt to make edits if you feel the edit are too minor.. and without which answer could be read/understandable .. – Rookie Programmer Aravind May 01 '13 at 05:12
5

I forgot to tell a bug when i use I use req.session.email = req.param('email'), the server error says cannot sett property email of undefined.

The reason of this error is a wrong order of app.use. You must configure express in this order:

app.use(express.cookieParser());
app.use(express.session({ secret: sessionVal }));
app.use(app.route);
Donald Lian
  • 61
  • 1
  • 2
5

It is cumbersome to interoperate socket.io and connect sessions support. The problem is not because socket.io "hijacks" request somehow, but because certain socket.io transports (I think flashsockets) don't support cookies. I could be wrong with cookies, but my approach is the following:

  1. Implement a separate session store for socket.io that stores data in the same format as connect-redis
  2. Make connect session cookie not http-only so it's accessible from client JS
  3. Upon a socket.io connection, send session cookie over socket.io from browser to server
  4. Store the session id in a socket.io connection, and use it to access session data from redis.
gustavohenke
  • 40,997
  • 14
  • 121
  • 129
nponeccop
  • 13,527
  • 1
  • 44
  • 106
2

Steps I did:

  1. Include the angular-cookies.js file in the HTML!
  2. Init cookies as being NOT http-only in server-side app.'s:

    app.configure(function(){
       //a bunch of stuff
       app.use(express.cookieSession({secret: 'mySecret', store: store, cookie: cookieSettings}));```
    
  3. Then in client-side services.jss I put ['ngCookies'] in like this:

    angular.module('swrp', ['ngCookies']).//etc

  4. Then in controller.js, in my function UserLoginCtrl, I have $cookies in there with $scope at the top like so:

    function UserLoginCtrl($scope, $cookies, socket) {

  5. Lastly, to get the value of a cookie inside the controller function I did:

    var mySession = $cookies['connect.sess'];

Now you can send that back to the server from the client. Awesome. Wish they would've put this in the Angular.js documentation. I figured it out by just reading the actual code for angular-cookies.js directly.

Ikhlak S.
  • 8,578
  • 10
  • 57
  • 77
CommaToast
  • 11,370
  • 7
  • 54
  • 69
0

Hello I am trying to add new session values in node js like

req.session.portal = false
Passport.authenticate('facebook', (req, res, next) => {
    next()
})(req, res, next)

On passport strategies I am not getting portal value in mozilla request but working fine with chrome and opera

FacebookStrategy: new PassportFacebook.Strategy({
    clientID: Configuration.SocialChannel.Facebook.AppId,
    clientSecret: Configuration.SocialChannel.Facebook.AppSecret,
    callbackURL: Configuration.SocialChannel.Facebook.CallbackURL,
    profileFields: Configuration.SocialChannel.Facebook.Fields,
    scope: Configuration.SocialChannel.Facebook.Scope,
    passReqToCallback: true
}, (req, accessToken, refreshToken, profile, done) => {
    console.log(JSON.stringify(req.session));
alexander.polomodov
  • 5,396
  • 14
  • 39
  • 46
Varun Sharma
  • 105
  • 1
  • 12