socketio / socketio/socket.io

Authorize/Block events depending on users permissions

Open
#4,712 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

documentation
Dominant language
TypeScript
Stars
63.2k
Forks
10.3k
Avg merge
11d 20h
Merged PRs (30d)
2

Description

Suppose we have many clients connected, but each one has a set of permissions, how can we block/allow the execution of of all connected listeners without adding the code to check authorizations in each event handler. something that maybe close to what I am talking about is prependAnyOutgoing.
I think something like an express middle ware would be very useful in lot of situations, it should provide the option to intercept all events and can block other listener or update data before it get sent to an event handler.

notifyOutgoingListeners is making a copy of all _anyOutgoingListeners before calling them one by one and it is using an iterator, so it is not wise or possible to override the connected events and set them again to the eventName via a setTimeout for example.


  /**
   * Notify the listeners for each packet sent (emit or broadcast)
   *
   * @param packet
   *
   * @private
   */
  private notifyOutgoingListeners(packet: Packet) {
    if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {
      const listeners = this._anyOutgoingListeners.slice();
      for (const listener of listeners) {
        listener.apply(this, packet.data);
      }
    }
  }

The only way to do what I am looking for was to override the socket.on function before setting any event handler.

const eventsRequiredPrivileges = {
    event_a: [permissions.event_a],
    event_b: [permissions.event_a],
}
// save the original socket.on function
const socketOn = socket.on;
// override socket.on
socket.on = function (eventName) {
    const notifyNotAutorized = () => {
        workspace.to(userID).emit("notification", { error: "not authorized" });
    }
    const args = [...arguments];
    // permissions map can change in real time (for example the admin change the authorizations of the user)
    if (!isAutorized(eventsRequiredPrivileges[eventName], permissionsMap)) {
        // override the event handler code with notifyNotAutorized
        // this will get called when the action is performed and not when we set the event 
        args.splice(1, 1, notifyNotAutorized);
    }
    socketOn.apply(this, args)
}
socket.on(event_a, () => {
    // do something that require permissions.event_a
});

socket.on(event_b, () => {
    // do something that require permissions.event_b
});

Without this we would have to write in each event handler

socket.on(event_a, () => {
    if (!isAutorized(eventsRequiredPrivileges[event_a], permissionsMap)) {
         return workspace.to(userID).emit("notification", { error: "not authorized" });
    }
    // do something that require permissions.event_a
});

The motivation behind doing the first way is that the possibility to manage all authorizations from single location to make the code more manageable and avoid duplication.

I would love to see this functionality implemented but I am not sure how it should look but I know for sure that it would be very useful in a lot of use cases.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading notifyOutgoingListeners, prependAnyOutgoing, and the socket.on usage shown in the issue. Compare how incoming event handlers and outgoing listeners are registered and invoked. Done would mean a defined, maintainable authorization interception API that can block or modify event handling without duplicating checks in every handler.

Written by the indexing model from the issue text.

Assessment

Tech stack
nodejs, typescript
Domain
authorization, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.