Authenticating ActionCable connections

旧时模样 提交于 2019-11-30 21:59:14

From the Readme

# app/channels/application_cable/connection.rb
module 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 current_user = User.find(cookies.signed[:user_id])
          current_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

So it looks like you could insert your own find_verified_user logic here. The reject_unauthorized_connection method lives in lib/action_cable/connection/authorization.rb for reference.

From Heroku:

[authentication] can be done in a variety of ways, as WebSockets will pass through standard HTTP headers commonly used for authentication. This means you could use the same authentication mechanism you’re using for your web views on WebSocket connections as well.

Since you cannot customize WebSocket headers from JavaScript, you’re limited to the “implicit” auth (i.e. Basic or cookies) that’s sent from the browser. Further, it’s common to have the server that handles WebSockets be completely separate from the one handling “normal” HTTP requests. This can make shared authorization headers difficult or impossible.

With this in mind it would likely be a real pain not to just use a normal web login flow to set your auth cookie, delivering your SPA after the authentication step, but hopefully this can give you some pointers.

FYI, if you have devise already installed in your application, then you can use the environment variable set by warden to find the authenticated user. For every authenticated user warden stores the user object in environment var. Every request is authenticated by warden middleware.

Note: this env is different from ENV.

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user_from_env
    end

    private
    def find_verified_user_from_env
      # extracting `user` from environment var
      current_user = env['warden'].user
      if current_user
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

If you have not used devise then, here is another solution. Precondition is, you will have to set a signed cookie called user_id in your sessions_controller or something like that. eg

cookies.signed[:user_id] = current_user.id

and for connection:

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user_from_cookies
    end

    private
    def find_verified_user_from_cookies
      current_user = User.find_by_id(cookies.signed[:user_id])
      if current_user
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

The solution is to use HTTP authorization token. It's simple, widespread and obvious. This article helped me a lot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!