SocketCluster/socketcluster-client

waitForAuth does not wait

Opened this issue · 5 comments

I'm trying to auth a user and then subscribe a channel using {waitForAuth: true}. Here is the client code:

var socket = socketCluster.create(options);
  socket.emit('auth', 'test');
  var channel = socket.subscribe('my-channel', {waitForAuth: true});
  channel.watch((e) => {
    console.log(JSON.stringify(e));
  })

And the server code is as described in https://socketcluster.io/#!/docs/authentication

The problem is that the MIDDLEWARE_PUBLISH_IN always runs before auth because of the db code and the user fails to subscribe; authToken is always null.

It works when I change to

socket.emit('auth', 'test', function (err) {
  if (!err) {
    var channel = socket.subscribe('my-channel');
    channel.watch((e) => {
      console.log(JSON.stringify(e));
    });
  }
});

which is quite spaghetti.
So the problem is that waitForAuth is not actually waiting anything. What am I doing wrong?

@coderproject waitForAuth delays the channel subscription until the socket is authenticated (which means that the socket has a valid JWT token - The SC server checks the validity of the JWT as part of the socket handshake).

Note that waitForAuth doesn't stop the current socket or anyone else from publishing to that channel in the meantime.

Are you sure that the socket is not already authenticated (check socket.authState on the client)? It's possible that the authentication happens much quicker the second time you refresh the page since it may still have a valid token from a previous session. Note that once you've issued a JWT token then it will be valid until its expiry date is reached or until the server's authKey changes.

SC's JWT auth mechanism means that the socket doesn't need to go through the whole authentication process from scratch every time the user refreshes the page (since the JWT is stored in localStorage by default). It reduces the load on the database. The point of JWT is that once the server has issued a signed token to a client, then it can trust that the client is who they claim to be (without having to check the DB every time).

But if you prefer to do the full DB authentication every time then you probably shouldn't use JWT; in that case you can just pass your custom token/session id as a querystring argument when creating the client socket and then use the MIDDLEWARE_HANDSHAKE_WS middleware on the back end to block the socket if the token in the querystring is not valid.

Ok I need to add more details, I have posted here the simplified version but now I can confirm that the posted code works:

var socket = socketCluster.create(options);
  socket.emit('auth', 'test');
  var channel = socket.subscribe('my-channel', {waitForAuth: true});
  channel.watch((e) => {
    console.log(JSON.stringify(e));
  })

I was testing with 2 clients before and seems like the bug exists only when multiple clients are created:

  var socket = socketCluster.create(options);
  socket.emit('auth', 'test');

  var socket2 = socketCluster.create(options);
  socket2.emit('auth', 'test2');
  var channel = socket2.subscribe('my-channel', {waitForAuth: true});
  channel.watch((e) => {
    console.log(JSON.stringify(e));
  })

As you see I am creating a socket, authenticating it, then creating another socket and authenticating it too, then subscribing with second socket, which fails. Please note that the tokens are different, clients are different users.

If this is by design, you may close this issue, I won't use 2 clients on the same page anyway, it was just a test. But looks like there's something wrong with waitForAuth when using multiple clients.

@coderproject a socket will pick up a JWT from localStorage if available. That means that if you create a second socket and it sees an existing JWT then it will use it to authenticate itself. So in your case, from the beginning, the second socket will be authenticated with the JWT of the first socket.

Now, that clarifies my issue. For debugging and testing cases, there could be an option to override this behaviour or could be added to docs as a warning. It caused me waste hours figuring out why subbing does not wait for auth. Never mind, sc is glorious. You can close this issue, or keep it for the docs advice. Happy coding.

@coderproject It's possible to change the client-side auth engine to make the client store the JWT elsewhere (e.g. in memory) but it's not as trivial as setting a boolean option.