Authenticating ActionCable connections

2020-03-04 03:24发布

I've found a wonderful ActionCable gem, which is a good solution for SPA.

I want to send only the html, css and js assets, all other connections will be implemented through ActionCable. It's not difficult to exchange strings or integers, but how can I login through ActionCable?

3条回答
做自己的国王
2楼-- · 2020-03-04 04:06

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
查看更多
在下西门庆
3楼-- · 2020-03-04 04:17

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

查看更多
可以哭但决不认输i
4楼-- · 2020-03-04 04:19

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.

查看更多
登录 后发表回答