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 toio = 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 ?
Solution 1:
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 thesession
through thehandshake
Solution 2:
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.
Solution 3:
This worked for me with
- express 4.9.0
- express.io 1.1.13
- connect-redis 2.1.0
- express-session 1.8.2
What I wanted was to share sessions with a frontend and backend API through redis. Separate machines, sharing same DB. Sessions are created and users logged in when they open page on the frontend, then the api looks up the logged in users on requests.
var cookieParser = require('cookie-parser')();
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
var db = require('./db')(config);
var sessionStore = session( {
store: new RedisStore({ client: db }),
secret: SECRET,
cookie: { secure: false }
}
);
app.use(cookieParser);
app.use(sessionStore);
// attach sessions to pure websocket requests
app.io.use(function(req, next) {
var res = {};
cookieParser(req, res, function(err) {
if (err) { return next(err); }
sessionStore(req, res, next);
});
});
Note: I set the cookie.secure to false so I can test without https locally.
Solution 4:
express 4.13.4 / socket.io 1.4.5
I browse all solutions and modules, but them all not working in my app. Finaly -
app.use(session({
secret: COOKIE_SECRET,
resave: true,
saveUninitialized: true,
store:sessionStore,
cookie: { domain: 'localhost',secure: false }
}));
io.use(function(socket, next) {
session({
secret: COOKIE_SECRET,
resave: true,
saveUninitialized: true,
store:sessionStore,
cookie: { domain: 'localhost',secure: false }
})(socket.handshake, {}, next);
});
working like a charm.
Solution 5:
This may work express 4 / socket.io 1.X I grabbed this code form https://github.com/turbonetix/bus.io/blob/master/demo/chat/app.js
io.use(function (socket, next) {
var handshake = socket.handshake;
if (handshake.headers.cookie) {
cookieParser()(handshake, {}, function (err) {
handshake.sessionID = connect.utils.parseSignedCookie(handshake.cookies[config.session.key], config.session.secret);
handshake.sessionStore = config.session.store;
handshake.sessionStore.get(handshake.sessionID, function (err, data) {
if (err) return next(err);
if (!data) return next(new Error('Invalid Session'));
handshake.session = new session.Session(handshake, data);
next();
});
});
}
else {
next(new Error('Missing Cookies'));
}
});