Sharing Session Socket.io Express - node.js

I am using the express-socket.io-session and express-mysql-session to share a session between socket.io and express. I do this as follows:
var express = require('express');
var mysql = require('mysql');
var app = express();
var serv = require('http').Server(app);
var io = require('socket.io')(serv, {});
var MySQLStore = require('express-mysql-session');
var options = {
host: "...",
user: "...",
password: "...",
database: "...",
};
var con = mysql.createConnection(options);
con.connect(function(err) {
if (err) throw err;
console.log("Connected!");
});
var sessionStore = new MySQLStore({},con);
var session = require("express-session")({
secret: "...",
resave: true,
saveUninitialized: true,
store : sessionStore
});
var sharedsession = require("express-socket.io-session");
// Use express-session middleware for express
app.use(session);
// Use shared session middleware for socket.io
// setting autoSave:true
io.use(sharedsession(session, {
autoSave:true
}));
Next, I have the following:
app.get('/', function(req, res, next){
console.log(req.session.sessionId);
console.log(req.session.username);
});
Finally:
socket.on('login', function(data) {
socket.handshake.session.sessionId = sessionId;
socket.handshake.session.username = result[0].username;
socket.handshake.session.save();
}
When I check my database, these values are saved correctly. However,
console.log(req.session.sessionId);
console.log(req.session.username);
always prints undefined, even after the login has occurred.

Related

swagger not preserving sessions

I am trying to set up a simple login endpoint in swagger:
//login.js
'use strict';
function login(req, res){
const userId = req.swagger.params.user_id.value;
const user = {
_id: userId
}
if(req.session.userId){
return res.json([req.session.userId, 'hello']);
}
global.app.db.collection('users').insertOne(user, function(err, b){
req.session.userId = userId;
return res.json([req.session]);
});
}
module.exports = {
login: login
};
It seems the if statement is never true, and the userId is never set permanently (it shows up in just the second return statement after the insert statement).
Not sure what is wrong here. My app.js has the typical express-session setup.
//app.js
'use strict';
const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
const mongoDbUrl = 'mongodb://localhost:27017';
const dbName = 'my_db';
var SwaggerExpress = require('swagger-express-mw');
var app = require('express')();
var session = require('express-session')
const MongoStore = require('connect-mongo')(session);
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: 'some secret',
store: new MongoStore({url:mongoDbUrl}),
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
module.exports = app; // for testing
global.app = app;
var config = {
appRoot: __dirname // required config
};
SwaggerExpress.create(config, function(err, swaggerExpress) {
if (err) { throw err; }
// install middleware
swaggerExpress.register(app);
var port = process.env.PORT || 10010;
app.listen(port);
if (swaggerExpress.runner.swagger.paths['/hello']) {
console.log('try this:\ncurl http://127.0.0.1:' + port + '/hello?name=Scott');
}
});
MongoClient.connect(mongoDbUrl, function(err, client) {
assert.equal(null, err);
console.log("Connected successfully to server");
const db = client.db(dbName);
app.db = db;
});
Looks like the problem was this line:
cookie: { secure: true }
secure should have been false because I wasn't using https on my local machine.

express-session session is not consistent

In my project i am using nodejs (express), react js and socket.io to develop a chatting application. But i am not able to get "correct sessionid" inside my socket function (io.use).
In server.js file there are routes and functions to handle socket connections.
In my project i had started my express-session inside login.js file.
Then after successfull login...i am redirecting to home.js, which intern returns home.html.
home.html contains js file, which is having react code and also have socket connections.
server.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var path = require('path');
var DIST_DIR = path.join(__dirname, "bin");
var express = require('express');
//Getting session store
var session = require('express-session');
var cookieParser = require('cookie-parser');
var sessionStore = new session.MemoryStore();
var handshakeData ={};
var cookie = require('cookie');
//Importing routes
var login = require('./routes/login.js');
var registration = require('./routes/registration.js');
var home = require('./routes/home.js');
//Getting cookies from request header. so that from it we can get sessionid.
io.use(function(socket, next) {
handshakeData = socket.request;
var cookies = cookie.parse(handshakeData.headers.cookie);
//Bringing sessionid and storing in a global variable
console.log("***********************Server*********************");
console.log("");
console.log('All cookies parsed in io.use ( %s )', JSON.stringify(cookies));
handshakeData.sessionID = cookies['connect.sid'].split('.')[0].split(':'[1];
console.log('All cookies parsed at server ( %s )', JSON.stringify(cookies));
console.log('Session id at server cookie value in io.use( %s )',
JSON.stringify(handshakeData.sessionID));
next();
});
//Bringing session data by sending "sessionid" to sessionStore
(MemoryStore)place
io.on('connection', function(socket){
sessionStore.get(handshakeData.sessionID, function (err, session) {
handshakeData.session = session;
//Now we can retrieve all session data. But sessionID sent was
not correct
});
console.log('user connected');
socket.on('new message', function(msg){
console.log('Server recieved new message: '+msg);
io.emit('new message', msg);
});
socket.on('disconnect', function(){
console.log('user disconnected');
});
});
app.use('/', login);
app.use('/registration', registration);
app.use('/home', home);
http.listen(8080, function(){
console.log("listening on port 8080");
});
login.js
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: false })
var cookieParser = require('cookie-parser');
var session = require('express-session');
var sessionStore = new session.MemoryStore();
var login_success = 0;
router.use(cookieParser());
router.use(session({ cookie: {
maxAge : 24*60*60*1000
},
store: sessionStore,
saveUninitialized: true,
resave: true,
secret: '1234567890QWERT'
}));
router.get('/', function(req, res){
console.log("Actual session id before login: "+ req.sessionID);
//Clearing cookies at client side.
res.clearCookie("login_message");
res.clearCookie("connect.sid");
res.clearCookie("io");
res.clearCookie("user_name");
res.clearCookie("user_id");
if (login_success == 2)
{
res.cookie('login_message', 'incorrectCredentials');
login_success = 0;
}
res.sendFile(path.join(SRC_DIR,"login.html"));
// res.sendFile(path.join(__dirname, '../bin', 'login.html'));
});
router.post('/', urlencodedParser, function(req, res){
console.log("database called");
//Fetching form data
var username = req.body.loginusername;
var password = req.body.loginpassword;
//Connection string to datanbase
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/WebChat";
//Comparing username and password.
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var query = { email: username, password: password };
db.collection("Profile").find(query).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
if (result.length != 0)
{
console.log("login success");
login_success = 1;
//Setting session variables
req.session.userid = username;
req.session.username= result[0].firstname;
console.log("***********************Login*********************");
console.log("");
console.log("Actual session id at login: "+ req.sessionID);
console.log("Session userid at server set to :"+ req.session.userid);
console.log("Session name at server set to :"+ req.session.username);
//res.sendFile(path.join(SRC_DIR,"home.html"));
// res.status(200).send(req.session);
console.log("Session reload called");
res.redirect('/home');
}
else
{
//**It is for when user come to login page with entering wrong credentials
login_success = 2;
console.log("login failed");
res.redirect('http://localhost:8080');
}
});
});
});
//export this router to use in our server.js
module.exports = router;
home.js
var express = require('express');
var router = express.Router();
var path = require("path");
var SRC_DIR=path.join(__dirname, "../src/views");
var session = require('express-session');
cookie = require('cookie');
var cookieParser = require('cookie-parser');
var sessionStore = new session.MemoryStore();
var handshakeData ={};
router.use(cookieParser());
router.use(session({ cookie: {
maxAge : 24*60*60*1000
},
store: sessionStore,
saveUninitialized: true,
resave: true,
secret: '1234567890QWERT'
}));
router.get('/', function(req, res){
//setting cookie at client side
res.clearCookie("user_id");
res.clearCookie("user_name");
res.cookie('user_id', req.session.userid);
res.cookie('user_name', req.session.username);
// res.status(200).send(req.session);
console.log(" session reload called");
setTimeout(function(){
res.sendFile(path.join(SRC_DIR,"home.html"));
}, 5000);
console.log("***********************Home*********************");
console.log("");
console.log("Actual session id at home: "+ req.sessionID);
console.log("Cookie userid at client set to :"+ req.session.userid);
console.log("Cookie username at client set to :"+ req.session.username);
});
//export this router to use in our server.js
module.exports = router;
home.jsx (client side file)
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {messages: [],socket: io.connect('http://localhost:8080')};
this.send = this.send.bind(this)
}
}
export default Home
Ouput when exicuting "node server.js":
output
In the above output...
1. login page we got two different session ids (before login and after login).I don't know why this is happening
2. While accessing the session variable inside "server.js", in "io.use" function... we again getting old session id, which was set before login.But with this i can't get my session variable. Because i need to pass new session variable in order to access session variable inside sessionStore.get.
Please help. i have been trying this for 3 days.
Thanks in advance.

MongoError: not authorized on (DB name) to execute command

I'm using Node.Js, Expressjs, mLab (to host my database) and mongoDB
Im getting an error of not being Authorized on Streetinform(my DB name) to execute command. {listIndexes: "sessions", cursor: {}}
this is the error.
this is how I connect to my Database
var dbHost = process.env.DB_HOST || 'localhost';
var dbPort = process.env.DB_PORT || 3002;
var dbName = process.env.DB_NAME || 'Mongodb';
var dbURL = 'mongodb://'+dbHost+':'+dbPort+'/'+dbName;
if (app.get('env') == 'development'){
// prepend url with authentication credentials //
dbURL = 'mongodb://'+process.env.DB_USER+':'+process.env.DB_PASS+'#'+dbHost+':'+dbPort+'/'+dbName;
}
app.use(session({
secret: 'secret',
proxy: true,
resave: true,
saveUninitialized: true,
store: new MongoStore({ url: dbURL })
})
);
this is my library
var http = require('http');
var express = require('express');
var session = require('express-session');
var bodyParser = require('body-parser');
var errorHandler = require('errorhandler');
var cookieParser = require('cookie-parser');
var MongoStore = require('connect-mongo')(session);
var dotenv = require('dotenv')
dotenv.load();
var app = express();
Establish Connection to Database
var crypto = require('crypto');
var MongoDB = require('mongodb').Db;
var Server = require('mongodb').Server;
var moment = require('moment');
/*
ESTABLISH DATABASE CONNECTION
*/
var dbName = process.env.DB_NAME || 'mongodb';
var dbHost = process.env.DB_HOST || 'localhost'
var dbPort = process.env.DB_PORT || 3002;
var db = new MongoDB(dbName, new Server(dbHost, dbPort, {autoReconnect: true}), {w: 1});
db.open(function(err, d){
if (err) {
console.log(err);
} else {
if (process.env.NODE_ENV == 'live') {
db.authenticate(process.env.DB_USER, process.env.DB_PASS, function(e, res) {
if (e) {
console.log('mongo :: error: not authenticated', e);
}
else {
console.log('mongo :: authenticated and connected to database :: "'+dbName+'"');
}
});
} else{
console.log('mongo :: connected to database :: "'+dbName+'"');
}
}
});
I to use mLabs to, it appears that you have not created a database uses due to which it cannot execute any commands.
You can enable it using the following steps:
Go to https://mlab.com/home
Click on the database which you are using from the Development and Utility
Click on the Users tab
Click the Add database user button
Enter you desired credentials
So now when you are authenticating pass in the username and password that you have entered in step 5

How to share sessions with Socket.IO 1.x and Express 4.x?

How can I share a session with Socket.io 1.0 and Express 4.x? I use a Redis Store, but I believe it should not matter. I know I have to use a middleware to look at cookies and fetch session, but don't know how. I searched but could not find any working
var RedisStore = connectRedis(expressSession);
var session = expressSession({
store: new RedisStore({
client: redisClient
}),
secret: mysecret,
saveUninitialized: true,
resave: true
});
app.use(session);
io.use(function(socket, next) {
var handshake = socket.handshake;
if (handshake.headers.cookie) {
var str = handshake.headers.cookie;
next();
} else {
next(new Error('Missing Cookies'));
}
});
The solution is surprisingly simple. It's just not very well documented. It is possible to use the express session middleware as a Socket.IO middleware too with a small adapter like this:
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
Here's a full example with express 4.x, Socket.IO 1.x and Redis:
var express = require("express");
var Server = require("http").Server;
var session = require("express-session");
var RedisStore = require("connect-redis")(session);
var app = express();
var server = Server(app);
var sio = require("socket.io")(server);
var sessionMiddleware = session({
store: new RedisStore({}), // XXX redis server config
secret: "keyboard cat",
});
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res || {}, next);
});
app.use(sessionMiddleware);
app.get("/", function(req, res){
req.session // Session object in a normal request
});
sio.sockets.on("connection", function(socket) {
socket.request.session // Now it's available from Socket.IO sockets too! Win!
});
server.listen(8080);
Just a month and a half ago I dealt with the same problem and afterwards wrote an extensive blog post on this topic which goes together with a fully working demo app hosted on GitHub. The solution relies upon express-session, cookie-parser and connect-redis node modules to tie everything up. It allows you to access and modify sessions from both the REST and Sockets context which is quite useful.
The two crucial parts are middleware setup:
app.use(cookieParser(config.sessionSecret));
app.use(session({
store: redisStore,
key: config.sessionCookieKey,
secret: config.sessionSecret,
resave: true,
saveUninitialized: true
}));
...and SocketIO server setup:
ioServer.use(function (socket, next) {
var parseCookie = cookieParser(config.sessionSecret);
var handshake = socket.request;
parseCookie(handshake, null, function (err, data) {
sessionService.get(handshake, function (err, session) {
if (err)
next(new Error(err.message));
if (!session)
next(new Error("Not authorized"));
handshake.session = session;
next();
});
});
});
They go together with a simple sessionService module I made which allows you to do some basic operations with sessions and that code looks like this:
var config = require('../config');
var redisClient = null;
var redisStore = null;
var self = module.exports = {
initializeRedis: function (client, store) {
redisClient = client;
redisStore = store;
},
getSessionId: function (handshake) {
return handshake.signedCookies[config.sessionCookieKey];
},
get: function (handshake, callback) {
var sessionId = self.getSessionId(handshake);
self.getSessionBySessionID(sessionId, function (err, session) {
if (err) callback(err);
if (callback != undefined)
callback(null, session);
});
},
getSessionBySessionID: function (sessionId, callback) {
redisStore.load(sessionId, function (err, session) {
if (err) callback(err);
if (callback != undefined)
callback(null, session);
});
},
getUserName: function (handshake, callback) {
self.get(handshake, function (err, session) {
if (err) callback(err);
if (session)
callback(null, session.userName);
else
callback(null);
});
},
updateSession: function (session, callback) {
try {
session.reload(function () {
session.touch().save();
callback(null, session);
});
}
catch (err) {
callback(err);
}
},
setSessionProperty: function (session, propertyName, propertyValue, callback) {
session[propertyName] = propertyValue;
self.updateSession(session, callback);
}
};
Since there is more code to the whole thing than this (like initializing modules, working with sockets and REST calls on both the client and the server side), I won't be pasting all the code here, you can view it on the GitHub and you can do whatever you want with it.
express-socket.io-session
is a ready-made solution for your problem. Normally the session created at socket.io end has different sid than the ones created in express.js
Before knowing that fact, when I was working through it to find the solution, I found something a bit weird. The sessions created from express.js instance were accessible at the socket.io end, but the same was not possible for the opposite. And soon I came to know that I have to work my way through managing sid to resolve that problem. But, there was already a package written to tackle such issue. It's well documented and gets the job done. Hope it helps
Using Bradley Lederholz's answer, this is how I made it work for myself. Please refer to Bradley Lederholz's answer, for more explanation.
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io');
var cookieParse = require('cookie-parser')();
var passport = require('passport');
var passportInit = passport.initialize();
var passportSession = passport.session();
var session = require('express-session');
var mongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
var sessionMiddleware = session({
secret: 'some secret',
key: 'express.sid',
resave: true,
httpOnly: true,
secure: true,
ephemeral: true,
saveUninitialized: true,
cookie: {},
store:new mongoStore({
mongooseConnection: mongoose.connection,
db: 'mydb'
});
});
app.use(sessionMiddleware);
io = io(server);
io.use(function(socket, next){
socket.client.request.originalUrl = socket.client.request.url;
cookieParse(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
socket.client.request.originalUrl = socket.client.request.url;
sessionMiddleware(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
passportInit(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
passportSession(socket.client.request, socket.client.request.res, next);
});
io.on('connection', function(socket){
...
});
...
server.listen(8000);
Working Example for PostgreSQL & Solving the problem of getting "an object with empty session info and only cookies":
Server-Side (Node.js + PostgreSQL):
const express = require("express");
const Server = require("http").Server;
const session = require("express-session");
const pg = require('pg');
const expressSession = require('express-session');
const pgSession = require('connect-pg-simple')(expressSession);
const PORT = process.env.PORT || 5000;
const pgPool = new pg.Pool({
user : 'user',
password : 'pass',
database : 'DB',
host : '127.0.0.1',
connectionTimeoutMillis : 5000,
idleTimeoutMillis : 30000
});
const app = express();
var ioServer = require('http').createServer(app);
var io = require('socket.io')(ioServer);
var sessionMiddleware = session({
store: new RedisStore({}), // XXX redis server config
secret: "keyboard cat",
});
io.use(function(socket, next) {
session(socket.request, {}, next);
});
app.use(session);
io.on("connection", socket => {
const ioSession = socket.request.session;
socket.on('userJoined', (data) => {
console.log('---ioSession---', ioSession)
}
}
Client-Side (react-native app):
To solve the problem of getting "empty session object" you need to add withCredentials: true
this.socket = io(`http://${ip}:5000`, {
withCredentials: true,
});
I have kinda solved it, but it is not perfect. Does not support signed cookies etc. I used express-session 's getcookie function. The modified function is as follows:
io.use(function(socket, next) {
var cookie = require("cookie");
var signature = require('cookie-signature');
var debug = function() {};
var deprecate = function() {};
function getcookie(req, name, secret) {
var header = req.headers.cookie;
var raw;
var val;
// read from cookie header
if (header) {
var cookies = cookie.parse(header);
raw = cookies[name];
if (raw) {
if (raw.substr(0, 2) === 's:') {
val = signature.unsign(raw.slice(2), secret);
if (val === false) {
debug('cookie signature invalid');
val = undefined;
}
} else {
debug('cookie unsigned')
}
}
}
// back-compat read from cookieParser() signedCookies data
if (!val && req.signedCookies) {
val = req.signedCookies[name];
if (val) {
deprecate('cookie should be available in req.headers.cookie');
}
}
// back-compat read from cookieParser() cookies data
if (!val && req.cookies) {
raw = req.cookies[name];
if (raw) {
if (raw.substr(0, 2) === 's:') {
val = signature.unsign(raw.slice(2), secret);
if (val) {
deprecate('cookie should be available in req.headers.cookie');
}
if (val === false) {
debug('cookie signature invalid');
val = undefined;
}
} else {
debug('cookie unsigned')
}
}
}
return val;
}
var handshake = socket.handshake;
if (handshake.headers.cookie) {
var req = {};
req.headers = {};
req.headers.cookie = handshake.headers.cookie;
var sessionId = getcookie(req, "connect.sid", mysecret);
console.log(sessionId);
myStore.get(sessionId, function(err, sess) {
console.log(err);
console.log(sess);
if (!sess) {
next(new Error("No session"));
} else {
console.log(sess);
socket.session = sess;
next();
}
});
} else {
next(new Error("Not even a cookie found"));
}
});
// Session backend config
var RedisStore = connectRedis(expressSession);
var myStore = new RedisStore({
client: redisClient
});
var session = expressSession({
store: myStore,
secret: mysecret,
saveUninitialized: true,
resave: true
});
app.use(session);
Now, the original accepted answer doesn't work for me either. Same as #Rahil051, I used express-socket.io-session module, and it still works. This module uses cookie-parser, to parse session id before entering express-session middleware.
I think it's silmiar to #pootzko, #Mustafa and #Kosar's answer.
I'm using these modules:
"dependencies":
{
"debug": "^2.6.1",
"express": "^4.14.1",
"express-session": "^1.15.1",
"express-socket.io-session": "^1.3.2
"socket.io": "^1.7.3"
}
check out the data in socket.handshake:
const debug = require('debug')('ws');
const sharedsession = require('express-socket.io-session');
module.exports = (server, session) => {
const io = require('socket.io').listen(server);
let connections = [];
io.use(sharedsession(session, {
autoSave: true,
}));
io.use(function (socket, next) {
debug('check handshake %s', JSON.stringify(socket.handshake, null, 2));
debug('check headers %s', JSON.stringify(socket.request.headers));
debug('check socket.id %s', JSON.stringify(socket.id));
next();
});
io.sockets.on('connection', (socket) => {
connections.push(socket);
});
};

ExpressJS & Websocket & session sharing

I'm trying to make a chat application based on Node.js. I'd like to force websocket server (ws library) to using ExpressJS session system. Unfortunately, I've got stuck. MemoryStore hashes used to get sessions' data are different than session IDs in cookies. Could somebody explain me what I'm doing wrong?
Websocket server code part:
module.exports = function(server, clients, express, store) {
server.on('connection', function(websocket) {
var username;
function broadcast(msg, from) {...}
function handleMessage(msg) {...}
express.cookieParser()(websocket.upgradeReq, null, function(err) {
var sessionID = websocket.upgradeReq.cookies['sid'];
//I see same value in Firebug
console.log(sessionID);
//Shows all hashes in store
//They're shorter than sessionID! Why?
for(var i in store.sessions)
console.log(i);
store.get(sessionID, function(err, session) {
websocket.on('message', handleMessage);
//other code - won't be executed until sessionID in store
websocket.on('close', function() {...});
});
});
});
}
store object definition:
var store = new express.session.MemoryStore({
reapInterval: 60000 * 10
});
app configuration:
app.configure(function() {
app.use(express.static(app.get("staticPath")));
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({
store: store,
secret: "dO_ob",
key: "sid"
}));
});
Part of main code:
var app = express();
var httpServer = http.createServer(app);
var websocketServer = new websocket.Server({server: httpServer});
httpServer.listen(80);
Sample debugging output:
- websocket.upgradeReq.headers.cookie "sid=s%3A64a%2F6DZ4Mab8H5Q9MTKujmcw.U8PJJIR%2BOgONY57mZ1KtSPx6XSfcn%2FQPZ%2FfkGwELkmM"
- websocket.upgradeReq.cookies["sid"] "s:64a/6DZ4Mab8H5Q9MTKujmcw.U8PJJIR+OgONY57mZ1KtSPx6XSfcn/QPZ/fkGwELkmM"
- i "64a/6DZ4Mab8H5Q9MTKujmcw"
I found this works for me. Not sure it's the best way to do this though. First, initialize your express application:
// whatever your express app is using here...
var session = require("express-session");
var sessionParser = session({
store: session_store,
cookie: {secure: true, maxAge: null, httpOnly: true}
});
app.use(sessionParser);
Now, explicitly call the session middleware from the WS connection. If you're using the express-session module, the middleware will parse the cookies by itself. Otherwise, you might need to send it through your cookie-parsing middleware first.
If you're using the websocket module:
ws.on("request", function(req){
sessionParser(req.httpRequest, {}, function(){
console.log(req.httpRequest.session);
// do stuff with the session here
});
});
If you're using the ws module:
ws.on("connection", function(req){
sessionParser(req.upgradeReq, {}, function(){
console.log(req.upgradeReq.session);
// do stuff with the session here
});
});
For your convenience, here is a fully working example, using express, express-session, and ws:
var app = require('express')();
var server = require("http").createServer(app);
var sessionParser = require('express-session')({
secret:"secret",
resave: true,
saveUninitialized: true
});
app.use(sessionParser);
app.get("*", function(req, res, next) {
req.session.working = "yes!";
res.send("<script>var ws = new WebSocket('ws://localhost:3000');</script>");
});
var ws = new require("ws").Server({server: server});
ws.on("connection", function connection(req) {
sessionParser(req.upgradeReq, {}, function(){
console.log("New websocket connection:");
var sess = req.upgradeReq.session;
console.log("working = " + sess.working);
});
});
server.listen(3000);
I was able to get this working. I think you need to specify the secret on cookieParser instead of session store.
Example from my app:
var app = express();
var RedisStore = require('connect-redis')(express);
var sessionStore = new RedisStore();
var cookieParser = express.cookieParser('some secret');
app.use(cookieParser);
app.use(express.session({store: sessionStore}));
wss.on('connection', function(rawSocket) {
cookieParser(rawSocket.upgradeReq, null, function(err) {
var sessionID = rawSocket.upgradeReq.signedCookies['connect.sid'];
sessionStore.get(sessionID, function(err, sess) {
console.log(sess);
});
});
});
Feb 2022 update:
verifyClient is now discouraged. New methods of doing this is described in an issue comment.
Consult the example code for session parsing and verification for a full usage example. Sample of the verification function:
server.on('upgrade', function (request, socket, head) {
console.log('Parsing session from request...');
sessionParser(request, {}, () => {
if (!request.session.userId) {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
socket.destroy();
return;
}
console.log('Session is parsed!');
wss.handleUpgrade(request, socket, head, function (ws) {
wss.emit('connection', ws, request);
});
});
});
Original answer:
In version 3.2.0 of ws you have to do it a bit differently.
There is a full working example of express session parsing in the ws repo, specifically using a new feature verifyClient.
A very brief usage summary:
const sessionParser = session({
saveUninitialized: false,
secret: '$eCuRiTy',
resave: false
})
const server = http.createServer(app)
const wss = new WebSocket.Server({
verifyClient: (info, done) => {
console.log('Parsing session from request...')
sessionParser(info.req, {}, () => {
console.log('Session is parsed!')
done(info.req.session.userId)
})
},
server
})
wss.on('connection', (ws, req) => {
ws.on('message', (message) => {
console.log(`WS message ${message} from user ${req.session.userId}`)
})
})
WS v3.0.0 and above, has changed the behaviour so the given answers won't work out of the box for those versions. For current versions, the signature of the connection method is [function(socket, request)] and the socket no longer contains a reference to the request.
ws.on(
'connection',
function (socket, req)
{
sessionParser(
req,
{},
function()
{
console.log(req.session);
}
);
}
);
Currently, below is my workaround which is working fine. I just don't know it's disadvantages and security. I just prevent the server from listening if it doesn't have a session. (Share session from express-session to ws)
I haven't fully tested this though.
var http = require('http');
var express = require('express');
var expressSession = require('express-session');
var router = express.Router();
var app = express();
const server = http.createServer(app);
router.get('/', function(req, res, next) {
if(req.session.user_id) {
// Socket authenticated
server.listen(8080, function listening(){});
}
});

Resources