0

I'm learning basics of Socket.io and I want to implement connection ONLY upon when user logs in. Currently, the offical documentation doesn't really seem to mention this kind of implementation. I'm using JWT for authentication and Node.js for server side.

Currently, Socket.io connects upon website visit:

const express = require('express');
const app = express();
const server = app.listen( process.env.PORT || 5555 );

const io = require('socket.io')(server, {
   cors: {
      origin: "*",
      methods: ["GET", "POST"]
   }
});

io.on('connection', socket => {
   console.log('A client connected but not logged in yet.');
   
   socket.on('disconnect', () => {
      console.log('A client disconnected');
   });
});

I want Socket.io to connect only when user logs in:

/** Example Logic **/

if (user_is_logged_in) {
   const io = require('socket.io')(server, {
      cors: {
         origin: "*",
         methods: ["GET", "POST"]
      }
   });
}

Best way to implement this? or is it even possible? Thanks.

passionateLearner
  • 722
  • 1
  • 7
  • 19
  • 1
    Is the JWT stored on the client side (e.g. localstorage)? – Owl Nov 18 '20 at 18:31
  • 1
    no, server is public and requires you to implement any user auth.. you can pass the jwt as a param in the connection, then implement the check in a middleware, just like you would in your app – Lawrence Cherone Nov 18 '20 at 18:32
  • @Owl Yes, it's stored in client side via localStorage. – passionateLearner Nov 18 '20 at 18:36
  • you shouldn't use localstorage to store jwt's, its not safe from XSS or *your bro* stealing it [see](https://stackoverflow.com/questions/44133536/is-it-safe-to-store-a-jwt-in-localstorage-with-reactjs) use http only secure cookies – Lawrence Cherone Nov 18 '20 at 18:37
  • @LawrenceCherone Oh really? That's how I was taught to do. Is there any particular reason why? – passionateLearner Nov 18 '20 at 18:39
  • 1
    IMO it's fine to store your JWT in localStorage, just make sure you escape user input (modern frontend frameworks should do this for you), if you store your JWT in cookie but your website is vulnerable XSS, you are doomed anyway, "`If your site runs a malicious script, it's game over anyway. They can just bind keydown events to inputs of type password and steal your user's authentication information that way (which is much, much worse than stealing a JWT auth token).`" from one of the comment on the question shared by @LawrenceCherone – Owl Nov 18 '20 at 19:00
  • @Owl the copy/pasted the comment is wrong (did you not read the answer?), rarely a XSS would be achievable on login page, so capturing login details would be 1 in a trillion. xss's happen sometimes even to big sites but allowing the hack to steal every logged-in users auth token would be game over if you're running a professional site, IMO better safe then fired lol – Lawrence Cherone Nov 18 '20 at 19:17

1 Answers1

2

The way to do this is check whether user is logged in or not after connect.

On the client

const jwt = getJWTFromLocalStorage(); // assume it returns undefined if user is not logged in
if (jwt) {
    const socket = io("http://example.com", {
        query: {
            token: jwt
        })
    };
}

And on the server side

io.on('connection', socket => {
    const token = socket.handshake.query.token; // jwt token passed from client

    // authenticate
    try {
        if (!token) throw new Error("Token not found");
        jwt.verify(token, yourJWTSecret)
    } catch (err) {
        // jwt verification failed
        socket.emit("authFailed") // emits event to client to let client know authentication failed, optional.
        socket.disconnect(); // disconnect client 
    }
   
    socket.on('disconnect', () => {
        console.log('A client disconnected');
    });
});

Keep in mind that passing JWT from the URL query string is not a safe method, I recommend implementing something else for authentication like this

Owl
  • 6,337
  • 3
  • 16
  • 30
  • yup or `socket.handshake.cookie` if using cookies – Lawrence Cherone Nov 18 '20 at 18:56
  • Would it be possible to send data from client instead of query? So maybe something like ```const socket = io('URL', { someData: 'token value' });```? – passionateLearner Nov 18 '20 at 19:07
  • 1
    No, but you can pass data from `.emit` so you can call `socket.emit("authorize", token)` on the client side after connected and on the server side handle it with `socket.on("authorize", (token) => { do something with token });`, if you are using this method, you need to make sure unauthorized `socket` won't receive any data / emission from the server, maybe you can create a separate array of `sockets` and only store authorized socket there. – Owl Nov 18 '20 at 19:10
  • 1
    Or you can make socket room on server side called `authentiated` and only emits to socket inside that room. More about room [here](https://socket.io/docs/v3/rooms/index.html) – Owl Nov 18 '20 at 19:12