How to achieve a scalable web socket solution using httponly jwt tokens and passport js

Viewed 36

My team and I have added an automatic check in/out mechanism to a set of pages of an application using Socket.IO. In development environments that utilise a single container instance everything works as expected. When a user opens one of these pages the page is checked out, when a disconnection occurs or the user navigates away from the page we're able to recall the userId from the socket within the disconnect event on the server:


checkOutPage = async (data, socket) => {
    try {
        const pageIsCheckedOut = await this.pageLockService.isPageCheckedOut(
            data.pageId
        );

        if (!pageIsCheckedOut) {
            await this.pageLockService.createLock(data);
            const locks = await this.pageLockService.getAllLocks();
            socket.broadcast.emit(UPDATE_LOCKS, { store: locks });
        }
    } catch (err) {
        logger.error(err);
    }
};

checkInPage = async (data, socket) => {
    try {
        await this.deleteForUser(data.userId);
        const locks = await this.pageLockService.getAllLocks();
        socket.broadcast.emit(UPDATE_LOCKS, { store: locks });
    } catch (err) {
        logger.error(err);
    }
};

register = async (data, socket) => {
    try {
        socket.userId = data;
        const locks = await this.pageLockService.getAllLocks();
        socket.emit(UPDATE_LOCKS, { store: locks });
    } catch (err) {
        logger.error(err);
    }
};

disconnect = async socket => {
    try {
        await this.deleteForUser(socket.userId);

        const locks = await this.pageLockService.getAllLocks();
        socket.emit(UPDATE_LOCKS, { store: locks });
        socket.removeAllListeners();
    } catch (err) {
        logger.error(err);
    }
};

async deleteForUser(userId) {
    try {
        if (userId) {
            await this.pageLockService.deleteLockByUserId(userId);
        }
    } catch (err) {
        logger.error(err);
    }
}

setEvents() {
    this.ioServer.on('connection', socket => {
      socket.on(REGISTER, data => this.register(data, socket));
      socket.on(USER_DISCONNECT, () => this.disconnect(socket));
      socket.on(CHECK_OUT_PRODUCT, data => this.checkOutPage(data, socket));
      socket.on(CHECK_IN_PRODUCT, data => this.checkInPage(data, socket));
    });
  }

The issues arise when we start talking about instance scaling. Now and then when a user disconnects the socket.userId is undefined. A note about our setup is that we use a JWT Passport strategy. Which I guess rules out using something like https://github.com/jfromaniello/passport.socketio due to the lack of a session store.

We also use HttpOnly jwt tokens due to security requirements for the project.

If anyone has any ideas or suggestions how to ensure socket data is persisted across containers that would be amazing!

0 Answers
Related