Send custom data along with handshakeData in socket.io?
Asked Answered
P

7

61

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)

Phlebotomize answered 6/12, 2012 at 14:18 Comment(0)
G
149

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()
});
Galloping answered 18/12, 2012 at 19:48 Comment(14)
Thanks! Exactly what I wanted! +1Phlebotomize
Is it possible to also send client some data back with a handshake?Bottle
@RobFox IIRC Rob I don't think it is, but you can write to the channel immediately after it's created and that will be the first thing the connecting client would see.Galloping
Excellent. This helped me out immensely. Thank you.Catricecatrina
It's worth noting that this implies sending the data as a querystring, which is not advisable if that data is supposed to be secret (i.e. passwords or authorization tokens).Rap
@FacundoOlano, how would one acchieve this differently? Regardless of querystring, postvars or as a special header, the token is sniffable if no https is used. But I'd really like to know your solution to mitigate such a MIM attackFatalism
@Fatalism you can't expect any security without https; but even with https, sending tokens in the querystring is not advisable. My workaround is sending an authentication message and putting the token in the message body. I've explained it hereRap
@AlexWright: How can I pass 2 parameters with query option? Thanks in advance.!Verulamium
@Verulamium You'd just append the second parameter to the querystring (in the example foo=bar). So an example with two parameters could be foo=bar&baz=whatever. Consider using encodeURIComponent() to encode the value if it has anything that's not legal or expected in a URL.Galloping
@AlexWright your answer doens't work with newer socket.io versions, could you update it to reflect my answer (https://mcmap.net/q/218526/-send-custom-data-along-with-handshakedata-in-socket-io)Insouciant
The .set() method is deprecatedKhedive
Its also worth noting that you can use an object instead of an encoded string. eg: instead of { query: "foo=bar" } you can do { query: { foo: "bar" } }Urinal
@Urinal Is this maybe only since the API change? I tried that on the pre-1.0 instance and it broke trying to .split on an &.Galloping
@AlexWright Yes this is with the latest, installed yesterdayUrinal
W
19

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();
});
Wyandotte answered 24/9, 2014 at 14:12 Comment(2)
how do I get the query field from the socket.request on the server side?Lazo
@HarryMoreno Use socket.request._queryKrystynakshatriya
I
10

For socket.io v1.2.1 use this:

io.use(function (socket, next) {
  var handshake = socket.handshake;
  console.log(handshake.query);
  next();
});
Insouciant answered 22/12, 2014 at 15:31 Comment(0)
N
7

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);
}
Nicolasanicolau answered 29/3, 2014 at 20:7 Comment(2)
This was the best solution. But socket.manager.handshaken[socket.id].query.user should be socket.manager.handshaken[socket.id].query.loggeduserDicky
This probably is version dependent. I could only get it to work with socket.handshake.query.userOrWhateverGrillo
L
1

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.

Lazo answered 26/2, 2015 at 6:39 Comment(1)
the author answered from slack github.com/Automattic/socket.io/issues/2022Lazo
J
1

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.

Johnathan answered 26/1, 2019 at 6:51 Comment(0)
B
0

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
}
Blackcap answered 27/5, 2014 at 1:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.