ActionCable - Part 3 - Securing Your WebSockets

#39 ActionCable - Part 3 - Securing Your WebSockets
7/24/2016

Summary

Using authentication and authorization, lock down your ActionCable WebSockets so that messages are only sent and received to the intended users.
4
rails view websockets security

Summary

layouts/application.html.erb<%= action_cable_meta_tag if user_signed_in? %>
assets/javascripts/cable.jsif ($('meta[name=action-cable-url]').length){
  (function() {
      this.App || (this.App = {});
      App.cable = ActionCable.createConsumer($('meta[name=action-cable-url]').attr('content'));
  }).call(this);
}
assets/javascripts/channels/chat.jsif ($('meta[name=action-cable-url]').length){
  App.chat = App.cable.subscriptions.create("ChatChannel", {
    connected: function() {
      // Called when the subscription is ready for use on the server
    },

    disconnected: function() {
      // Called when the subscription has been terminated by the server
    },

    received: function(data) {
      // Called when there's incoming data on the websocket for this channel
      $('.messages').prepend(data['message']);
    },
  });
}
app/channels/application_cable/connection.rbmodule ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected
    
    def find_verified_user
      if verified_user = env['warden'].user
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

# User Logs On (typically in a session#new)
# cookies.signed['user.id'] = user.id
# cookies.signed['user.expires_at'] = 30.minutes.from_now

# ActionCable Connection
# def find_verified_user
#   verified_user = User.find_by(id: cookies.signed['user.id'])
#   if verified_user && cookies.signed['user.expires_at'] > Time.now
#     verified_user
#   else
#     reject_unauthorized_connection
#   end
# end

# User Logs Off (typically in a session#destroy)
# cookies.signed['user.id'] = nil
# cookies.signed['user.expires_at'] = nil
messages_controller.rbclass MessagesController < ApplicationController
  before_action :authenticate_user!
  after_action :verify_authorized
  def create
    message = current_user.messages.new(params[:message].permit!)
    authorize message
    message.save
  end
end
message.rbclass Message < ApplicationRecord
  belongs_to :user
  after_create_commit { MessageBroadcastJob.perform_later(self) }
end
app/channels/chat_channel.rbclass ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chat" if current_user.email == [email protected]'
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end
end