How to use sessionID with express and socket.io - node.js

I am trying to hold session data across my whole web-app, but I have difficulties to get the same sessionID everywhere.
What's the use of the sessionID inside of this snippet of code (used with express):
app.get('/test-page', function(req, res) {
console.log(req.sessionID);
});
Every time I open the page I get a new sessionID here.
Furthermore, I am using this code for getting the sessionID from the cookie in my socket.io:
io.set('authorization', function(handshakeData, accept) {
if (handshakeData.headers.cookie) {
handshakeData.cookie = parseCookie(handshakeData.headers.cookie);
handshakeData.sessionID = handshakeData.cookie['express.sid'];
} else {
handshakeData.sessionID = -1;
}
accept(null, true);
});
io.sockets.on('connection', function(socket) {
console.log('client connected, ssID: ' + socket.handshake.sessionID);
});
How to get this same session in app.get('/test-page',... ? Or vice versa.

I never got to figuring this out but you could always do some rewriting to express.io
Checkout the sessions example, seems to be exactly what you need.

Related

How to get socket connect options in server? [duplicate]

So I have an application running node js with socket.io as a backend and normal javascript as frontend. My application has a login system which currently simply has the client send its login data as soon as it's connected.
Now I figured it would be much nicer to have the login data sent along with the handshakeData, so I can directly have the user logged in while connecting (instead of after establishing a connection) respectively refuse authorization when the login data is invalid.
I'm thinking it would be best to put my additional data in the header part of the handshakeData, so any ideas how I could do that? (Without having to modify socket.io if possible, but if it's the only way I can live with it)
As a lot of comments have pointed out below the Socket.IO API changed in their 1.0 release. Authentication should now be done via a middleware function, see 'Authentication differences' # http://socket.io/docs/migrating-from-0-9/#authentication-differences. I'll include my orginal answer for anyone stuck on <1.0 as the old docs seem to be gone.
1.0 and later:
Client Side:
//The query member of the options object is passed to the server on connection and parsed as a CGI style Querystring.
var socket = io("http://127.0.0.1:3000/", { query: "foo=bar" });
Server Side:
io.use(function(socket, next){
console.log("Query: ", socket.handshake.query);
// return the result of next() to accept the connection.
if (socket.handshake.query.foo == "bar") {
return next();
}
// call next() with an Error if you need to reject the connection.
next(new Error('Authentication error'));
});
Pre 1.0
You can pass a query: param in the second argument to connect() on the client side which will be available on the server in the authorization method.
I've just been testing it. On the client I have:
var c = io.connect('http://127.0.0.1:3000/', { query: "foo=bar" });
On the server:
io.set('authorization', function (handshakeData, cb) {
console.log('Auth: ', handshakeData.query);
cb(null, true);
});
The output on the server then looked like:
:!node node_app/main.js
info - socket.io started
Auth: { foo: 'bar', t: '1355859917678' }
Update
3.x and later
You can pass an authentication payload using the auth param as the second argument to connect() in the client side.
Client Side:
io.connect("http://127.0.0.1:3000/", {
auth: {
token: "AuthToken",
},
}),
In server side you can access it using socket.handshake.auth.token
Server Side:
io.use(function(socket, next){
console.log(socket.handshake.auth.token)
next()
});
This has now been changed in v1.0.0. See the migration docs
basically,
io.set('authorization', function (handshakeData, callback) {
// make sure the handshake data looks good
callback(null, true); // error first, 'authorized' boolean second
});
becomes :
io.use(function(socket, next) {
var handshakeData = socket.request;
// make sure the handshake data looks good as before
// if error do this:
// next(new Error('not authorized');
// else just call next
next();
});
For socket.io v1.2.1 use this:
io.use(function (socket, next) {
var handshake = socket.handshake;
console.log(handshake.query);
next();
});
This my code for sending query data to nodejs and server.io server client.
var socket = io.connect(window.location.origin, { query: 'loggeduser=user1' });
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.user);
}
Perhaps the api has changed but I did the following to get extra info to the server.
// client
io.connect('localhost:8080', { query: 'foo=bar', extra: 'extra'});
// server
io.use(function(sock, next) {
var handshakeData = sock.request;
console.log('_query:', handshakeData._query);
console.log('extra:', handshakeData.extra);
next();
});
prints
_query: { foo: 'bar',
EIO: '3',
transport: 'polling',
t: '1424932455409-0' }
extra: undefined
If anyone knows how to get data from a client to the server through the handshake that is not in the query params let me know please.
Update I ran into issues later with this syntax
io.connect('localhost:8080?foo=bar');
is what I'm currently using.
Old thread but assuming you store your jwt token/session id in session cookies (standard stuff) this gets passed to the server by default anyway when doing handshake (socket.io-client) I've noticed.
Is there anything wrong with just getting the auth information for the handshake (via middleware or on.connection) via cookie?
eg.
io.on('connection', function(socket) {
// assuming base64url token
const cookieStr = socket.handshake.headers.cookie
const matchRes =
cookieStr == null
? false
: cookieStr.match(/my-auth-token=([a-zA-Z0-9_.-]+)/)
if (matchRes) {
// verify your jwt...
if ( tokenIsGood(matchRes[1]) {
// handle authenticated new socket
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
}
I'm using this now for a project and it's working fine.
I found a little problem to see the .loggeduser
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.loggeduser);
// ↑ here
}

Socket.io add listeners in Express 4 routes

I declare socket server in separate module. I have access to the object of the server everywhere in application, for example I can emmit. But I can't add listener in the route, for example:
router.post('/example', function(req, res, next) {
var socketio = req.app.get('sock');
socketio.sockets.on('connection', function (socket) {
socket.on('message', function (text) {
console.log('ok');
});
});
Is there any way to do this?
If there is just a single device that you want to communicate with and it should have already connected to your server and req.app.get('sock') is how you get access to the socketio server object, then you can do it like this:
var theDeviceSocket;
req.app.get('sock').on('connection', function(socket) {
theDeviceSocket = socket;
});
router.post('/example', function(req, res, next) {
if (theDeviceSocket) {
theDeviceSocket.emit("someMsg", "someData");
}
// send whatever response you want to send
res.end();
});
If you were trying to get a response from the single device and return that as the response to the POST, then you could so something like this:
// store the one connection to/from the special device
var theDeviceSocket;
// keep a request cntr so we can tell which response goes with which request
var requestCntr = 0;
req.app.get('sock').on('connection', function(socket) {
theDeviceSocket = socket;
});
router.post('/example', function(req, res, next) {
var timer, thisRequestId;
function gotData(data) {
// if this is our specific response
if (data.requestId === thisRequestId) {
theDeviceSocket.removeListener("someMsgResponse", gotData);
res.send(data);
clearTimeout(timer);
}
}
if (theDeviceSocket) {
theDeviceSocket.on("someMsgResponse", gotData);
thisRequestId = requestCntr++;
theDeviceSocket.emit("someMsg", {requestId: thisRequestId});
// set up a timeout in case we don't get the proper response
timer = setTimeout(function() {
theDeviceSocket.removeListener("someMsgResponse", gotData);
res.send("error");
}, 5000);
}
});
This second scheme is complicated by the fact that you have architected this to be a request/response scheme, but socket.io is not a request/response protocol. So, in order to know which response belongs to which request (when there are potentially multiple clients interacting with the server at the same time), you have to implement some sort of requestId in the data you send and receive over socket.io. This means you have to change the device to echo back the requestId you sent it with its response. All of this would not be necessary if you used a protocol that is designed for request/response like HTTP instead of socket.io.

Express + Socket.io: find key in query string for every event except one

I'd like permit any socket connection to my server but in the case the client connects without specifying a 'username' in socket.handshake.query, I want to just let him create a new account and then the server should automatically disconnect him.
The only way I could achieve it was validating socket.handshake.query on every event except the signUp event -this means runing validateQuery() on almost every event!-. I feel that this may be a very rough way. I have also tryed to implement this with middleware but I was not able to achieve the desired behaviour (I'm pretty new to Node).
This is my code, please tell me if you know a better way to implement this :)
NOTE: A session handler is not mandatory for this case.
'use strict';
/*** BASE SETUP: Main objects & Dependencies */
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io');
// Set port in which the server will listen
var port = process.env.PORT || 3000;
// Start servers (HTTP is temporal and just for personal tests...)
var io = io.listen(server);
server.listen(port, function() {
console.log('>> Express server listening on port ' + port + ' <<');
});
/*
* A dummy socket validation function.
*/
var validateQuery = function(socket, next) {
console.log("> Verifying username...");
if ( typeof socket.handshake.query.username === 'undefined' || socket.handshake.query.username === "" ) {
console.log("-> Authentification failed!");
socket.disconnect();
return next(new Error("{ code: 403, description: 'You must specify a username in your query' }"));
}
};
/*** SOCKET IO Events */
io.sockets.on('connection', function(socket) {
console.log("> New connection stablished: "+ socket.id);
// # Disconnect
//
socket.on('disconnect', function() {
// clients.splice(clients.indexOf(socket.id), 1);
console.log("> Triggered 'Disconnect' # " + socket.id);
});
// # SignUp - Creates new users
//
socket.on('signUp', function( requestData ) {
console.log("> Triggered 'signUp' # " + socket.id);
socket.emit('onSignUp', { username : requestData.username, dsp : requestData.dsp });
socket.disconnect();
});
// # getUserList - Gets a list of all registered users
//
socket.on('getUserList', function( requestData ) {
// Validate query
validateQuery(socket);
// Send the user's list
socket.emit( 'onGetUserList', userList );
});
// # getUserProfile - Gets a user's profile. Will return the requester's profile if none specified
//
socket.on('getUserProfile', function( requestData ) {
// Validate username
validateQuery(socket);
var userProfile = { username : "User1", dsp : "Ron" };
socket.emit('onGetUserProfile', userProfile);
});
});
If you want to allow the connection without the auth, but then only allow some events to be processed if they are not authenticated, then you will have to check validation on every event that requires auth. There's no other way around it if you're going to allow some events through without auth.
You could make the auth check a little simpler though. When the socket first connects, you couuld do the validation and then set a custom flag on the socket object. From then on, you can just check that flag if an event requires auth (which should be a little more efficient).
If any follow on events might change the auth status, you can then update the flag.
You could also make some shortcuts for your event handlers that require auth.
function addAuthEventListener(sock, event, fn) {
sock.on(event, function (data) {
if ( typeof sock.handshake.query.username === 'undefined' || sock.handshake.query.username === "" ) {
sock.disconnect();
} else {
return fn.apply(this, arguments)
}
});
}
So, then any event handler that requires auth could just be installed like this:
addAuthEventListener(socket, 'onGetUserList', function(requestData) {
// Send the user's list
socket.emit( 'onGetUserList', userList );
});
And the auth checks would happen automatically.

Socket.io broadcasts everything to everybody in node/express app

I wondered if someone could help figure out what I am doing wrong:
My client web page initiates a connection with my server, and listens to a long running process whose state is getting updated in the db by a worker process on another thread, emitting updates back to the browser. I define a socket.io connection in the app.post() method. This is handled by the poll() function below (scroll down a bit past the invite checking code)
However, when a new web client connects, it's messages get added to the previous client's as if there were just one channel. Why isn't there a separate unique channel for each browser?
//Create server
var express = require('express'),
app = express(),
http = require('http'),
server = http.createServer(app),
io = require('socket.io').listen(server);
io.set('log level', 1); // reduce logging
io.configure(function () {
io.set("transports", ["xhr-polling"]);
io.set("polling duration", 10);
});
app.post('/api/users', function (req, res) {
if (!req.body.auth.accessToken) {
req.body.auth.accessToken = req.body.auth.authResponse.accessToken;
} //fb return object is different depending on whether it is a first login or subsequent
logger.log('debug', '/api/users:POST', req.body);
io.sockets.on('connection', function (socket) {
socket = socket;
socket.emit('update', {
status: 200 //send initialization ping
});
//check if user has valid invite, if not try to invite
db.getTotalUserInvites(function (err_inv, res_total) {
db.getUserInvite(req.body.fid, function (err_check, res_check) {
logger.log('debug', 'Total invites issued=' + res_total);
//process report - all we need is accesToken, processReport will do the rest
mine_fb.processUser(req.body.auth.accessToken, socket, function (User,socket) { //pass channel properly
db.getReportStatus(User.fid,socket, function (result,socket) {
logger.log('debug', 'report status', result);
if (result) {
if (socket && (result.report_status == -1)) {
logger.log('debug', 'report already processed. retrieving uniq_id ' + result.uniq_id);
socket.emit('update', {
status: -1,
uniq_id: result.uniq_id
});
return true;
} else {
if (socket && (result.report_status >= 0)) {
logger.log('debug', 'we are in the middle of processing report ' + result.uniq_id);
//in this case we become a listener and not a speaker
function poll(socket) {
db.getReportStatus(User.fid, socket,function (r,socket) {
socket.emit('update', { //!!!! THIS EMITS TO ALL CONNECTED BROWSERS
status: r.report_status,
uniq_id: r.uniq_id
}); //...socket
if ((r.report_status >= 0) && (socket)) {
logger.log('debug', 'polling...');
_.delay(poll, 2000, socket);
}
}); //get rerpot
}; //end poll
socket.on('disconnect', function () {
socket=null;
});
poll(socket);
} // else we're in the middle
} //done checking status
} //end of seq
});
return res.send();
});
});
});
});
});
While it is not clear how to help you I can tell what's going on in your code:
app.post('/api/users', function (req, res) {
// some code
io.sockets.on('connection', function (socket) {
// some code
});
});
Whenever a user POSTs something to /api/users a new handler is attached to io.sockets (that's what .on does). But these handlers are never removed, so each time a new connection is established all attached handlers fire. That's where your broadcasting comes from.
You have to separate app.post(...) from io.sockets.on('connection',...) (they should be independent, both defined at module level, not nested). I'm sure it won't be easy (you will probably have to authenticate a user twice for example) but that's the only reasonable way.
You shouldn't put your io.sockets.on('connection', function (socket) inside the app.post scope.
Just put it outside and try again, it will probably work correctly.
Listening to connexion should be done once when the server starts, not each time a client hits some URL.

Send custom data along with handshakeData in socket.io?

So I have an application running node js with socket.io as a backend and normal javascript as frontend. My application has a login system which currently simply has the client send its login data as soon as it's connected.
Now I figured it would be much nicer to have the login data sent along with the handshakeData, so I can directly have the user logged in while connecting (instead of after establishing a connection) respectively refuse authorization when the login data is invalid.
I'm thinking it would be best to put my additional data in the header part of the handshakeData, so any ideas how I could do that? (Without having to modify socket.io if possible, but if it's the only way I can live with it)
As a lot of comments have pointed out below the Socket.IO API changed in their 1.0 release. Authentication should now be done via a middleware function, see 'Authentication differences' # http://socket.io/docs/migrating-from-0-9/#authentication-differences. I'll include my orginal answer for anyone stuck on <1.0 as the old docs seem to be gone.
1.0 and later:
Client Side:
//The query member of the options object is passed to the server on connection and parsed as a CGI style Querystring.
var socket = io("http://127.0.0.1:3000/", { query: "foo=bar" });
Server Side:
io.use(function(socket, next){
console.log("Query: ", socket.handshake.query);
// return the result of next() to accept the connection.
if (socket.handshake.query.foo == "bar") {
return next();
}
// call next() with an Error if you need to reject the connection.
next(new Error('Authentication error'));
});
Pre 1.0
You can pass a query: param in the second argument to connect() on the client side which will be available on the server in the authorization method.
I've just been testing it. On the client I have:
var c = io.connect('http://127.0.0.1:3000/', { query: "foo=bar" });
On the server:
io.set('authorization', function (handshakeData, cb) {
console.log('Auth: ', handshakeData.query);
cb(null, true);
});
The output on the server then looked like:
:!node node_app/main.js
info - socket.io started
Auth: { foo: 'bar', t: '1355859917678' }
Update
3.x and later
You can pass an authentication payload using the auth param as the second argument to connect() in the client side.
Client Side:
io.connect("http://127.0.0.1:3000/", {
auth: {
token: "AuthToken",
},
}),
In server side you can access it using socket.handshake.auth.token
Server Side:
io.use(function(socket, next){
console.log(socket.handshake.auth.token)
next()
});
This has now been changed in v1.0.0. See the migration docs
basically,
io.set('authorization', function (handshakeData, callback) {
// make sure the handshake data looks good
callback(null, true); // error first, 'authorized' boolean second
});
becomes :
io.use(function(socket, next) {
var handshakeData = socket.request;
// make sure the handshake data looks good as before
// if error do this:
// next(new Error('not authorized');
// else just call next
next();
});
For socket.io v1.2.1 use this:
io.use(function (socket, next) {
var handshake = socket.handshake;
console.log(handshake.query);
next();
});
This my code for sending query data to nodejs and server.io server client.
var socket = io.connect(window.location.origin, { query: 'loggeduser=user1' });
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.user);
}
Perhaps the api has changed but I did the following to get extra info to the server.
// client
io.connect('localhost:8080', { query: 'foo=bar', extra: 'extra'});
// server
io.use(function(sock, next) {
var handshakeData = sock.request;
console.log('_query:', handshakeData._query);
console.log('extra:', handshakeData.extra);
next();
});
prints
_query: { foo: 'bar',
EIO: '3',
transport: 'polling',
t: '1424932455409-0' }
extra: undefined
If anyone knows how to get data from a client to the server through the handshake that is not in the query params let me know please.
Update I ran into issues later with this syntax
io.connect('localhost:8080?foo=bar');
is what I'm currently using.
Old thread but assuming you store your jwt token/session id in session cookies (standard stuff) this gets passed to the server by default anyway when doing handshake (socket.io-client) I've noticed.
Is there anything wrong with just getting the auth information for the handshake (via middleware or on.connection) via cookie?
eg.
io.on('connection', function(socket) {
// assuming base64url token
const cookieStr = socket.handshake.headers.cookie
const matchRes =
cookieStr == null
? false
: cookieStr.match(/my-auth-token=([a-zA-Z0-9_.-]+)/)
if (matchRes) {
// verify your jwt...
if ( tokenIsGood(matchRes[1]) {
// handle authenticated new socket
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
}
I'm using this now for a project and it's working fine.
I found a little problem to see the .loggeduser
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.loggeduser);
// ↑ here
}

Resources