2

Using websocket-rails, I can use the following code in my controller to tricker a websocket publish event:

WebsocketRails[:channel_name].trigger('event_name', { foo: "bar" }.to_json)

My goal is to create a pub-sub channel for the current user only. Take for example the event "new chat message sent". I want to push this event only the receiver's channel.

I'm currently making a unique channel name for each user based on their ID. I put the following code in my controller (having defined a current_user method elsewhere:

WebsocketRails[:"user#{current_user.id}"].trigger("event_name", { foo: "bar" }.to_json)

And then in my Javascript, I subscribe the current user to their own channel with the following:

<% if @current_user %>
  var dispatcher = new WebSocketRails('localhost:3000/websocket');
  channel = dispatcher.subscribe('user<%= @current_user.id %>'); 
  channel.bind('event_name', function(data) {
    console.log(data)
  });
<% end %>

The gist of it is using string interpolation to make a new channel for each user, i.e. user12 and user123 channels.

The problem is this is not really secure. Any user can access anyone else's private channel just by pasting some Javascript in. For example, if user #1 wants to access user #2's news feed, they could just type dispatcher.subscribe('user2').

How would you solve this issue? Is there another pub-sub library which has this feature built into it?

Looking on WebsocketRails' wiki entry on the subject, I tried adding the follolwing code to config/initializers/websockets.rb

WebsocketRails::EventMap.describe do
  namespace :websocket_rails do
    subscribe :subscribe_private, to: ConnectionsController, with_method: :authorize_channels
  end
end

And the following to app/controllers/connections_controller.rb

class ConnectionsController < WebsocketRails::BaseController
  def authorize_channels
    channel_name = WebsocketRails[message[:channel]]
    current_user = User.find_by(id: session["current_user_id"])
    if current_user && "user#{current_user.id}".eql?(channel_name)
      accept_channel current_user
    else
      deny_channel({ message: "auth failed" })
    end
  end
end

And then elsewhere, I'm calling WebsocketRails[:"user#{@current_user.id}"].make_private

This doesn't seem to have any effect though.

max pleaner
  • 26,189
  • 9
  • 66
  • 118

0 Answers0