ankane / pretender

Log in as another user in Rails

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ActionCable Integration

hwhelchel opened this issue · comments

I have a Rails 5 app that uses Pretender for impersonation.

Currently when impersonating an account, my action cable channels no longer work. What's the simplest cleanest way to add Pretender to action cable?

To authenticate my cable connection I use:

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

    def connect
      self.current_account = find_verified_account
    end

    private

    def find_verified_account
      if verified_account && cookies.signed['account_expires_at'] > Time.current
        verified_account
      else
        reject_unauthorized_connection
      end
    end

    def verified_account
      @verified_account ||= Account.find_by(id: cookies.signed[:account_id])
    end
  end
end

and here are the warden hooks which cookie the account_id

Warden::Manager.after_set_user do |account, auth, opts|
  scope = opts[:scope]

  auth.cookies.signed["#{scope}_id"] = account.id
  auth.cookies.signed["#{scope}_expires_at"] = 30.minutes.from_now
end

Warden::Manager.before_logout do |_account, auth, opts|
  scope = opts[:scope]

  auth.cookies.signed["#{scope}_id"] = nil
  auth.cookies.signed["#{scope}_expires_at"] = nil
end

I tried this naive solution which didn't work:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_account
    extend Pretender
    impersonates :account

    def connect
      self.current_account = find_verified_account
    end

    private

    def find_verified_account
      if verified_account && cookies.signed['account_expires_at'] > Time.current
        verified_account
      else
        reject_unauthorized_connection
      end
    end

    def verified_account
      @verified_account ||= Account.find_by(id: cookies.signed[:account_id])
    end
  end
end

I used #22 to solve my problem since I use Devise and Warden Constraints for managing my ActionCable cookie. I removed pretender for now but it is a great gem to quickly add impersonation!

Hey @hwhelchel, thanks for sharing for future readers 👍

FWIW I got this working by simply accessing the imposter ID in the session before falling back to the warden user (true user). Here is my Connection:

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

    def connect
      self.current_user = find_verified_user
      if self.current_user.nil?
        reject_unauthorized_connection
      end
    end

    def find_verified_user
      if imposter = request.session['impersonated_user_id']
        User.find imposter
      else
        env['warden'].user
      end
    rescue
      nil
    end
  end
end

Thanks for sharing @phosphor1879 👍 That's the missing piece I needed to add official support. You can follow these instructions with master branch.

thanks @ankane for adding these changes 👏