How to get / save session data in an authorization event in Socket.io/express-sessions?

I configure websocket using Socket.io and express 4 frameworks on node.js.

I am trying to complete the authorization step for my users when using my websocket.

When the user connects, the token is passed as the request value for the server. At the server level, I query the database for the session that matches the transferred token. If a session is detected, I will conduct several checks to make sure that the token is not captured.

Problem

Session data seems to be cleared each time the page is reloaded. Or the server does not associate sessionId with the user who created it, so every time it generates a new session.

I am puzzled by how to access the if-installed session variables.

Problem with my code

When the user reloads his page / client, the session data will become undefined in the new request. The session is good until the page refreshes, and that is my problem. I need to be able to maintain a session even after the user refreshes his page.

Questions

How can I guarantee that session data is not cleared with every page refresh?

Here is my authorization code

io.set('authorization', function (handshakeData, accept) { var session = handshakeData.session || {}; //This is always undefined! console.log('Session Data:' + session.icwsSessionId); //var cookies = handshakeData.headers.cookie; var token = handshakeData._query.tokenId || ''; //console.log('Token: ' + token); if(!token){ console.log('Log: token was not found'); return accept('Token was found.', false); } //allow any user that is authorized if(session && session.autherized && token == session.token){ console.log('Log: you are good to go'); return accept('You are good to go', true); } //if the client changed their token "client logged out" //terminate the open session before opening a new one if (session.autherized && token != session.token){ var icwsConnection = new icwsConn(icwsRequest); icwsRequest.setConnection(session.icwsServer, session.icwsPort); icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); icwsConnection.logout(); session.autherized = false; session.token = null; session.icwsServer = null; session.icwsPort = null; session.icwsSessionId = null; session.icwsToken = null; icwsConnection = null; } 

Here is my whole code if necessary

 var env = require('./modules/config'), app = require('express')(), https = require('https'), fs = require('fs'), session = require('express-session'), redisStore = require("connect-redis")(session), sharedsession = require("express-socket.io-session"), base64url = require('base64url'); const server = https.createServer( { key: fs.readFileSync('certs/key.pem'), cert: fs.readFileSync('certs/cert.pem') }, function (req, res){ res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); } ).listen(env.socket.port, env.socket.host, function () { console.log('\033[2J'); console.log('Websocket is running at https://%s:%s', server.address().address, server.address().port); }); var io = require('socket.io')(server); const sessionMiddleware = session({ store: new redisStore({ host: env.redis.host, port: env.redis.port }), secret: env.session.secret, name: env.session.name, rolling: false, resave: true, saveUninitialized: true }); app.use(sessionMiddleware); // Use shared session middleware for socket.io // setting autoSave:true io.use(sharedsession(sessionMiddleware, { autoSave: true })); var icwsReq = require('./modules/icws/request.js'), icwsConn = require('./modules/icws/connection.js'), icwsInter = require('./modules/icws/interactions.js'), sessionValidator = require('./modules/validator.js'); var clients = {}; var icwsRequest = new icwsReq(); var sessionChecker = new sessionValidator(); app.get('/', function (req, res) { res.send('welcome'); }); io.set('authorization', function (handshakeData, accept) { var session = handshakeData.session || {}; //This is always undefined! console.log('Session Data:' + session.icwsSessionId); //var cookies = handshakeData.headers.cookie; var token = handshakeData._query.tokenId || ''; //console.log('Token: ' + token); if(!token){ console.log('Log: token was not found'); return accept('Token was found.', false); } //allow any user that is authorized if(session && session.autherized && token == session.token){ console.log('Log: you are good to go'); return accept('You are good to go', true); } /* if (!originIsAllowed(origin)) { // Make sure we only accept requests from an allowed origin socket.destroy(); console.log((new Date()) + ' Connection from origin ' + origin + ' rejected.'); return false; } */ //if the client changed their token "client logged out" //terminate the open session before opening a new one if (session.autherized && token != session.token){ var icwsConnection = new icwsConn(icwsRequest); icwsRequest.setConnection(session.icwsServer, session.icwsPort); icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); icwsConnection.logout(); session.autherized = false; session.token = null; session.icwsServer = null; session.icwsPort = null; session.icwsSessionId = null; session.icwsToken = null; icwsConnection = null; } var myIP = '10.0.4.195'; var decodedToken = base64url.decode(token); sessionChecker.validateData(decodedToken, myIP, env.session.duration, function(isValid, icws){ if(isValid){ session.authorized = true; session.icwsServer = icws.host; session.icwsPort = icws.port; session.token = token; session.icwsSessionId = null; session.icwsToken = null; icwsRequest.setConnection(icws.host, icws.port); var icwsConnection = new icwsConn(icwsRequest); icwsConnection.login(icws.username, icws.password, function(isLogged, icwsSession, headers){ if(isLogged && icwsSession.sessionId && icwsSession.csrfToken){ //icwsConnection.setWorkstation(icws.workstaton); session.icwsSessionId = icwsSession.sessionId; session.icwsToken = icwsSession.csrfToken; icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); console.log('Log: new connection to ICWS! ' + session.icwsSessionId ); } }); console.log('Log: new connection to websocket!') return accept('New connection to websocket!', true); } else { console.log('Log: token could not be validated!'); return accept('Token could not be validated!', false); } }); }); io.on('connection', function (socket) { console.log('Authorized Session! Websocket id ready for action!'); //var origin = socket.request.headers.origin || ''; //var myIP = socket.request.socket.remoteAddress || ''; if(!socket.request.sessionID){ console.log('Missing Session ID'); return false; } var socketId = socket.id; var sessionID = socket.request.sessionID; //Add this socket to the user connection if(userCons.indexOf(socketId) == -1){ userCons.push(socketId); } clients[sessionID] = userCons; console.log(clients); //display all connected clients socket.on('placeCall', function(msg){ icwsInter.call(method, uri, params, header, true); }); socket.on('chat', function(msg){ console.log('Chat Message: ' + msg); socket.emit('chat', { message: msg }); }); socket.on('disconnect', function(msg){ console.log('Closing sessionID: ' + sessionID); var userCons = clients[sessionID] || []; var index = userCons.indexOf(socketId); if(index > -1){ userCons.splice(index, 1); console.log('Removed Disconnect Message: ' + msg); } else { console.log('Disconnect Message: ' + msg); } }); socket.on('error', function(msg){ console.log('Error Message: ' + msg); }); }); function originIsAllowed(origin) { // put logic here to detect whether the specified origin is allowed. var allowed = env.session.allowedOrigins || [] if(allowed.indexOf(origin) >= 0){ return true; } return false; } 

Edited

The io cookie changes with every request. When the io cookie is created, it will have the last available values 12/31/1969 4:00:00 PM

In addition, this cookie changes every time the page is reloaded.

After the @Osk suggestion below, here is my new code that still doesn't save my session data when the page reloads.

 var env = require('./modules/config'), app = require('express')(), https = require('https'), fs = require('fs'), session = require('express-session'), redisStore = require("connect-redis")(session), sharedsession = require("express-socket.io-session"), base64url = require('base64url'), cookieParser = require("cookie-parser"); const server = https.createServer( { key: fs.readFileSync('certs/key.pem'), cert: fs.readFileSync('certs/cert.pem') }, function (req, res){ res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); } ).listen(env.socket.port, env.socket.host, function () { console.log('\033[2J'); console.log('Websocket is running at https://%s:%s', server.address().address, server.address().port); }); var io = require('socket.io')(server); var sessionStore = new redisStore({ host: env.redis.host, port: env.redis.port }); const sessionMiddleware = session({ store: sessionStore, secret: env.session.secret, name: env.session.name, rolling: true, resave: false, saveUninitialized: false, cookie: { maxAge: 60 * 60 * 1000 } }); app.use(sessionMiddleware); // Use shared session middleware for socket.io // setting autoSave:true io.use(sharedsession(sessionMiddleware, { autoSave: false })); var icwsReq = require('./modules/icws/request.js'), icwsConn = require('./modules/icws/connection.js'), icwsInter = require('./modules/icws/interactions.js'), sessionValidator = require('./modules/validator.js'); var clients = {}; var icwsRequest = new icwsReq(); var sessionChecker = new sessionValidator(); app.get('/', function (req, res) { res.send('welcome'); }); //Middleware for authorizing a user before establishing a connection io.use(function(socket, next) { var origin = socket.request.headers.origin || ''; if (!originIsAllowed(origin)) { // Make sure we only accept requests from an allowed origin socket.destroy(); console.log((new Date()) + ' Connection from origin ' + origin + ' rejected.'); return false; } var myIP = socket.request.socket.remoteAddress || ''; var token = socket.handshake.query.tokenId || ''; var session = socket.handshake.session || {}; //This should be defined on a reload console.log('IP Address: ' + myIP + ' SessionID: ' + socket.handshake.sessionID); if(!token){ console.log('Log: token was not found'); return next(new Error('Token not found')); } //allow any user that is authorized if(session && session.autherized && token == session.token){ console.log('Log: you are good to go'); return next(new Error('You are good to go')); } //if the client changed their token "client logged out" //terminate the open session before opening a new one if (session.autherized && token != session.token){ var icwsConnection = new icwsConn(icwsRequest); icwsRequest.setConnection(session.icwsServer, session.icwsPort); icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); icwsConnection.logout(); session.autherized = false; session.token = null; session.icwsServer = null; session.icwsPort = null; session.icwsSessionId = null; session.icwsToken = null; icwsConnection = null; session.save(); } var decodedToken = base64url.decode(token); sessionChecker.validateData(decodedToken, myIP, env.session.duration, function(isValid, icws){ if(isValid){ session.authorized = true; session.icwsServer = icws.host; session.icwsPort = icws.port; session.token = token; session.icwsSessionId = null; session.icwsToken = null; icwsRequest.setConnection(icws.host, icws.port); var icwsConnection = new icwsConn(icwsRequest); /* icwsConnection.login(icws.username, icws.password, function(isLogged, icwsSession, headers){ if(isLogged && icwsSession.sessionId && icwsSession.csrfToken){ //icwsConnection.setWorkstation(icws.workstaton); session.icwsSessionId = icwsSession.sessionId; session.icwsToken = icwsSession.csrfToken; icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); console.log('Log: new connection to ICWS! ' + session.icwsSessionId ); } }); */ session.save(function(){ console.log('Log: new connection to websocket!'); }); return next(); } else { console.log('Log: token could not be validated!'); return next(new Error('Token could not be validated!')); } }); }); io.on('connection', function (socket) { console.log('Connection is validated and ready for action!'); var socketId = socket.id; if(!socket.handshake.sessionID){ console.log('sessionId was not found'); return false; } var sessionID = socket.handshake.sessionID; var userCons = clients[sessionID] || []; //Add this socket to the user connection if(userCons.indexOf(socketId) == -1){ userCons.push(socketId); } clients[sessionID] = userCons; //console.log(clients); socket.on('placeCall', function(msg){ icws.call(method, uri, params, header, true); }); socket.on('chat', function(msg){ console.log('Chat Message: ' + msg); socket.emit('chat', { message: msg }); }); socket.on('disconnect', function(msg){ console.log('Closing sessionID: ' + sessionID); var userCons = clients[sessionID] || []; var index = userCons.indexOf(socketId); if(index > -1){ userCons.splice(index, 1); console.log('Removed Disconnect Message: ' + msg); } else { console.log('Disconnect Message: ' + msg); } }); socket.on('error', function(msg){ console.log('Error Message: ' + msg); }); }); function originIsAllowed(origin) { // put logic here to detect whether the specified origin is allowed. var allowed = env.session.allowedOrigins || [] if(allowed.indexOf(origin) >= 0){ return true; } return false; } 
+7
javascript websocket express
source share
2 answers

What version of socket.io are you using?

express-socket.io-session works with socket.io 1.x

I see that you are calling io.set() , which is deprecated on socket.io 1.x

For more information, see http://socket.io/docs/migrating-from-0-9/ under the heading Authentication Differences. There he stated that

The old io.set() and io.get() methods are io.set() and only supported for backward compatibility. "

Could this be related to your problem?

When you install the express-socket.io-session package, there is an example directory inside the package. This may come in handy to test against a working example for this module.

+3
source share

Here is an example of how you can share sessions between express and socket.io, even if they are not in the same domain.

(You can find a slightly different git repository with an example here https://github.com/dievardump/express-socket-auth )

I just use express-session , I don't understand why use other middleware as it works fine with socket.io

Since I don't have redis available, I used require('session-file-store') for general sessions.

Problem

The problem arises from the cross-domain policy, which will not allow you to use the cookie connect.sid value.

Workaround:

  • serve non-httpOnly session cookies from the host (here server.dev is for me). [express.js line 16]

  • read using javascript and send the connect.sid value as the sessionId parameter when connecting to socket.io [client.js line 26:30]

  • when the handshake is added the value of connect.sid=socket.handshake.query.sessionId to socket.handshake.headers.cookie before reading the handshake with the session middleware [socket.js line 32:37]

Architecture

Here follows:

  • server.js that require

    • express.js: create an express server on my computer using http://server.devhaps000

      • submit HTML

      • create sessions on page load

    • socket.js: Create a Socket.io server, accessible on my computer through http://socket.dev:8000

  • client.js

    • submitted to http://server.dev:3000

    • connect to the socket server at http://socket.dev:8000

Test

The testing steps I used here are:

  • Client opens page

  • If the cookie connect.sid button is not set

    • Client is trying to connect to socket.io : Connection error: [Not authenticated]

    • The client calls /authenticate

      • session created
    • The client tries to connect to socket.io with the value connect.sid as the sessionId : Connection sucessfull parameter

  • If cookie connect.sid set

    • The client tries to connect to socket.io with the value of connect.sid as sessionId : Connection sucessfull

Files

server.js

 require('./express'); require('./socket'); 

express.js

  var express = require('express'); var app = express(); var http = require('http'); var io = require('socket.io'); var bodyParser = require('body-parser'); var sessionExpress = require('express-session'); var FileStore = require('session-file-store')(sessionExpress); var secret = 'keyboard cat'; var session = sessionExpress({ secret: secret, store: new FileStore(), resave: true, saveUninitialized: true, cookie: { httpOnly: false, // important to allow client to read session cookie with JavaScript maxAge: 60 * 60 * 1000 } }); app.use(bodyParser.urlencoded({ extended: true })); app.use(express.static(__dirname)); app.use('/authenticate', session); app.get('/authenticate', function(req, res) { var session = req.session; session.userdata = session.userdata || {}; session.userdata.connected = true; session.save(function(err) { if (err) { connectionError(res, session); } else { res.status(200); res.send(); } }); }); // routes app.get('/', function(req, res) { res.send('welcome'); }); // setup servers var server = http.createServer(app); server.listen(3000); 

socket.js

 var express = require('express'); var app = express(); var http = require('http'); var io = require('socket.io'); var sessionExpress = require('express-session'); var FileStore = require('session-file-store')(sessionExpress); var secret = 'keyboard cat'; var sessionIdKey = 'connect.sid'; var session = sessionExpress({ secret: secret, store: new FileStore(), resave: true, saveUninitialized: true, cookie: { maxAge: 60 * 60 * 1000 } }); // setup servers var server = http.createServer(app, function (req, res){ res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); }); server.listen(8000); var sio = io(server); sio.use(function(socket, accept) { // writing sessionId, sent as parameter, on the socket.handshake cookies if (socket.handshake.query.sessionId) { var cookies = (socket.handshake.headers.cookie || '').split(';'); cookies.push(sessionIdKey + '=' + socket.handshake.query.sessionId); socket.handshake.headers.cookie = cookies.join(';'); } session(socket.handshake, {}, function(err) { if (err) return accept(err); console.log('User trying to connect to Socket.io'); var session = socket.handshake.session, userData = session.userdata || {}; // is connected and good if (!userData || !userData.connected) { console.log('----- User has no active session, error'); accept(new Error('Not authenticated')); } else { console.log('----- Socket.io connection attempt successful'); accept(null, session.userid !== null); } }); }); sio.on('connection', function (socket) { console.log('Connection'); }); 

client.js

  function getCookie(name) { var value = "; " + document.cookie; var parts = value.split("; " + name + "="); if (parts.length == 2) return decodeURIComponent(parts.pop().split(";").shift()); } function fetch(url, data, callback) { try { var x = new XMLHttpRequest(); x.open(data ? 'POST' : 'GET', url, 1); x.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); x.onreadystatechange = function () { x.readyState > 3 && callback && callback(x.responseText, x); }; x.send(data || null); } catch (e) { window.console && console.log(e); } }; function connectServer(cb) { var sessionId = getCookie('connect.sid'); var data = { forceNew: true, query : { } }; if (sessionId) { data.query.sessionId = sessionId } console.log('Trying to connect to Socket.io server'); var server = io('http://socket.dev:8000', data); server.on('error', function (err) { console.log('----- Connection error : [%s]', err); setTimeout(function () { cb(); }, 200); }); server.on('connect', function (data) { console.log('----- Connection successful with sessionId [%s]', sessionId); setTimeout(function () { cb(); }, 200); }); } if (getCookie('connect.sid')) { console.log('Session cookie Detected'); connectServer(function () { }); } else { connectServer(function () { console.log('Call ./authenticate to create session server side'); fetch('./authenticate', null, function () { console.log('Session created') connectServer(function () {}); }); }); } 

Execution

First Page Load Results

Client:

 Trying to connect to Socket.io server ----- Connection error : [Not authenticated] Call ./authenticate to create session server side Session created Trying to connect to Socket.io server ----- Connection successful with sessionId [s:Ir9dVPi8wzplPCoeNXAsDlOkhL8AW0gx.wwzUQ2jftntWEc6lRdYqGxRBoszjPtjT4dBW/KjFIXQ] 

Server:

 User trying to connect to Socket.io ----- User has no active session, error User trying to connect to Socket.io ----- Socket.io connection attempt successful Connection 

Refresh the page

Client:

 Session cookie Detected Trying to connect to Socket.io server ----- Connection successful with sessionId [s:Ir9dVPi8wzplPCoeNXAsDlOkhL8AW0gx.wwzUQ2jftntWEc6lRdYqGxRBoszjPtjT4dBW/KjFIXQ] 

Server:

 User trying to connect to Socket.io ----- Socket.io connection attempt successful Connection 
+1
source share

All Articles