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 👏