Connect.sid is always undefined and req.sessionID

2020-07-25 07:59发布

I'm quite new to NodeJS/Express/Socket.IO and have made a simple rock-paper-scissors app using Socket.IO communication (on the client-side I'm using AngularJS). That part of the app worked quite fine so far.

However, I started working on implementing sessions using Redis and have stumbled upon some problems. Since I'm doing this to learn, I decided to do a part of the app using sockets (the whole real-time part of playing a game of rock-paper-scissors), and the other part which doesn't require the real-time component using regular REST (leaderboard, viewing other peoples profiles etc.).

The problem I'm having is that the connect.sid cookie is never set on the client. I made a simple checkSession REST call to test if the session is persisted but it seems that it's not. What I also noticed in checkSession call is that the req.sessionID always gives me a brand new sessionID. Now, I'm not sure if I'm supposed to create the connect.sid manually, or is node supposed to handle that automatically? If I'm supposed to do it, how should I do it? The "set authorization" callback also never has the connect.sid cookie in the handshake object.

Also, from what I read during researching this, it can be a bit troublesome to share sessions between regular REST (Express) and Socket.IO. Here is my code (I turned off the whole rock-paper-scissors logic to make sure that's not what's causing the problems).

package.json:

{
  "name": "RPSLS.Node",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "3.5.1",
    "session": "0.1.0",
    "socket.io": "0.9.16",
    "redis": "0.10.2",
    "connect-redis": "1.4.7"
  }
}

app.js:

var express = require('express');
var parseCookie = express.cookieParser("megaultrasupersecret");

var http = require('http');
var path = require('path');
var io = require('socket.io');

var app = express();
var server = http.createServer(app);
var ioServer = io.listen(server);

var redis = require('redis');
var RedisStore = require('connect-redis')(express);

// Route dependencies
var sharedData = require('./shared/data')();
var routes = require('./routes');

// Enable CORS
var allowCrossDomain = function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "http://rpsls.local" || "http://localhost" || "http://127.0.0.1");
    res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
    res.header("Access-Control-Allow-Headers", "Content-Type");
    next();
}

// All environments
app.set('port', process.env.PORT || 7331);
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser("megaultrasupersecret"));
app.use(express.session({ store: new RedisStore({ client: redis.createClient() }), key: "connect.sid", secret: "megaultrasupersecret", cookie: { maxAge: 24 * 60 * 60 * 1000 } }));
app.use(allowCrossDomain);
app.use(app.router);

ioServer.configure(function () {
    ioServer.set('store', new io.RedisStore({
        redisPub: redis.createClient(),
        redisSub: redis.createClient(),
        redisClient: redis.createClient()
    }));

    ioServer.set('authorization', function (handshakeData, callback) {
        console.log(handshakeData.headers.cookie); // always returns undefined

        parseCookie(handshakeData, null, function (err) {
            // both always returns undefined
            console.log(handshakeData.signedCookies['connect.sid']);
            console.log(handshakeData.cookies['connect.sid']);
        });

        callback(null, true); // error first callback style 
    });
});

app.get("/", routes.index);
app.get("/checkSession", routes.checkSession);

ioServer.sockets.on('connection', function (socket) {
    // ... socket logic ... 
});

server.listen(app.get('port'), function(){
    console.log("Express server listening on port " + app.get('port'));
});

The checkSession implementation:

module.exports = function () {
    return {
        index: function (req, res) {
            res.send("RPSLS.Node API is online.");
        },
        checkSession: function (req, res) {
            console.log("----------- SESSION:   ");
            console.log(req.session);
            console.log("----------- Session ID:   " + req.sessionID); // always a brand new sessionID
            console.log("----------- Session UserName:   " + req.session.userName); // never gets persisted
            console.log("----------- Connect.sid:   " + req.cookies["connect.sid"]); // always undefined
            console.log("----------- REQ cookies:   "); 
            console.log(req.cookies); // sometimes outputs some _ga cookie (have no clue what it's for), but nothing else

            req.session.userName = "someUserName";
            req.session.save(function () {
                res.send("TEST");
            });
        }
    };
};

Its console.log outputs:

----------- SESSION (this is what gets stored in redis):
{ cookie:
   { path: '/',
     _expires: Mon May 26 2014 22:56:23 GMT+0200 (Central European Daylight Time),
     originalMaxAge: 86400000,
     httpOnly: true } }
----------- Session ID:   7KIjYzdqfyZFCjDDT4bE1P5a
----------- Session UserName:   undefined
----------- Connect.sid:   undefined
----------- REQ cookies:  {}

The Angular HTTP call:

this.checkSession = function () {
    $http.get('http://localhost:7331/checkSession')
        .success(function (userName) {
            console.log(userName);
        });
};

Here are some keys from Redis to confirm it's working (I also successfully saved some other data to Redis in some other parts of the app but figured that's not relevant here):

127.0.0.1:6379> keys *
sess:Fe8ND2riR4jQEtCEmhYKi4hS
sess:WGLNkth7HW2PtR2AG4noId8Q
sess:Ph0aUwRWEtawEWxIrqVQj3Us
sess:RzLVnaLV8BPZqUsjCCzV9RCn
sess:ZfmMcGUyFf957IsVc1gkbXKb
sess:6zdD3PStbx1EmFtHjoxmVGkr
sess:QNyHy4PUNE21fBrgmjiyjNkq

One more thing I noticed in DevTools is the response cookie called connect.sid, which contains the session id and hashed secret. This session ID is always the new one that I get in checkSession. Am I supposed to set it on the client manually, or should this work out-of-the-box?

enter image description here

This is from the response headers:

Set-Cookie:connect.sid=s%3AvMXrIBuaheA7ZBkxql51HQfO.67KqBFY4DVS4rQ50jv2B6bn4V6LeLMpdMxcpP6P%2FXIc;
Path=/; Expires=Mon, 26 May 2014 21:19:05 GMT; HttpOnly

I spent a lot of time trying to figure this out but I'm left clueless on why this doesn't work. Everything is run on localhost. What am I not understanding and doing wrong, what am I missing?

edit1: As per mscdex's request, console.dir(req.headers) outputs the following:

{ host: 'localhost:7331',
  'user-agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0', accept: 'application/json, text/plain, */*',
  'accept-language': 'en-US,en;q=0.5',
  'accept-encoding': 'gzip, deflate',
  dnt: '1',
  referer: 'http://localhost:9000/',
  origin: 'http://localhost:9000',
  connection: 'keep-alive',
  'if-none-match': '"-286616648"',
  'cache-control': 'max-age=0' }

edit2: If it's of any importance, NodeJS app is running on localhost:7331, and I tried running AngularJS on localhost:9000 (through grunt), 127.0.0.1:9000 and rpsls.local (through MS IIS) with same results.

1条回答
迷人小祖宗
2楼-- · 2020-07-25 08:36

Try adding res.header("Access-Control-Allow-Credentials", "true"); to your allowCrossDomain middleware function. You may also look into using the cors middleware instead of handling CORS yourself.

Then modify your angular get call to add withCredentials: true to the request config: $http.get('http://localhost:7331/checkSession', { withCredentials: true }).

查看更多
登录 后发表回答