Actioncable authentication with multiple Devise mo

2019-08-30 19:27发布

问题:

I am currently implementing a chat between 2 of my Devise models. Client and Professionnel. It is working just fine at the moment but I have only one channel: every Client or Professionnel receive all messages from all clients and all professionals. The displaying is fine but someone who watches their AJAX flow can see every private message that is not meant for them..

As per this thread http://www.thegreatcodeadventure.com/rails-5-action-cable-with-multiple-chatroom-subscriptions/ this is called single responsibility principle

So I am trying to create "sub streams" in order to broadcast to the right users.

My first step is authing with Devise. I am using the classic :

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

    def connect
        self.current_user = find_verified_user
        logger.add_tags 'ActionCable', current_user.email       
    end

    protected

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

  end
end

And this is where the problem arises: when a Client is logged in, Actioncable is not creating a connection:

Started GET "/cable" for 127.0.0.1 at 2018-04-16 22:30:28 +0200
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2018-04-16 22:30:28 +0200
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: keep-alive, Upgrade, HTTP_UPGRADE: websocket)
An unauthorized connection attempt was rejected

But when a Professionel logs in everything is ok :

Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: keep-alive, Upgrade, HTTP_UPGRADE: websocket)
  Professionnel Load (0.4ms)  SELECT  "professionnels".* FROM "professionnels" WHERE "professionnels"."id" = $1 ORDER BY "professionnels"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
[ActionCable] [dummy@gmail.com] Registered connection (Z2lkOi8vcm9vZnNlZWRzL1Byb2Zlc3Npb25uZWwvMQ)

This is very strange as in both case the session cookie shows a warden related id, either when Client or Professionel is logged :

["session_id", "0c3649a0e924de3fbf14dbb1cf2ce058"] ["flash", {"discard"=>[], "flashes"=>{"notice"=>"Connecté."}}] ["professionnel_return_to", "/"] ["warden.user.client.key", [[1], "$2a$12$auNvgciv6qPDf7n5a93vXu"]]

["session_id", "43a9b6db09311473419d7f22f2ce6419"] ["flash", {"discard"=>[], "flashes"=>{"notice"=>"Connecté."}}] ["warden.user.professionnel.key", [[1], "$2a$12$qZ4whbN3e2w7fn8NJWa/B."]] ["professionnel_return_to", "/"] ["_csrf_token", "sZA8gze5lxU6R71XUci0uQDXVk+d75VslI90ImcjwhA="] 

I dont get where the error comes from ..

EDIT EDIT EDIT

Just found this piece of code in Devise initializer :

  # Configure the default scope given to Warden. By default it's the first
  # devise role declared in your routes (usually :user).
  # config.default_scope = :user

Professionnel is indeed my first Devise role declared in my routes. Does it has anything to do with my problem ? And How can I solve this ?

    devise_for :professionnels, controllers: {registrations: "professionnels/registrations", passwords: "professionnels/passwords", confirmations: "professionnels/confirmations"}
    devise_for :clients, controllers: {registrations: "clients/registrations", passwords: "clients/passwords", confirmations: "clients/confirmations"}
    devise_for :admins, controllers: {registrations: "admins/registrations", sessions: "admins/sessions"}

回答1:

Ok the answer is simple: if you have multiple Devise accounts to auth into Actioncable, you have to scope warden for each of them, in my case connection.rb looks like :

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

    def connect
        self.current_user = find_verified_user
        logger.add_tags 'ActionCable', current_user.email       
    end

    protected

    def find_verified_user
        if verified_user = env["warden"].user(:professionnel)
          verified_user
        elsif verified_user = env["warden"].user(:client)
          verified_user    
        else
          reject_unauthorized_connection
        end
    end


  end
end

In this case I give them the same identifier as the chatroom is a child of both of them so I ( guess I) can treat them the same. I think it is possible to give them different identifiers if needed.