Error in ActionCable with Apartment gem and Stimulus Reflex - ruby-on-rails

I'm developing a multi tenant app, in Rails 6 with ActionCable, the gem Apartment and Stimulus Reflex.
connection.rb
`
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
tenant = request.subdomain
Apartment::Tenant.switch!(tenant)
self.current_user = find_verified_user
logger.add_tags "ActionCable", "User #{current_user.id}", "Tenant #{tenant}"
end
protected
def find_verified_user
if current_user = env['warden'].user
current_user
else
reject_unauthorized_connection
end
end
end
end`
I have this, until now:
development.rb
config.hosts << ".lvh.me"
config.action_cable.url = 'ws://lvh.me:3000/cable'
config.action_cable.allowed_request_origins = ['http:\/\/*.lvh.me:3000*']
By implementing this line of code in connection.rb: logger.add_tags "ActionCable", "User #{current_user.id}", "Tenant #{tenant}" i can see in the console that everything it's ok at beginning, but as soon i call any Stimulus Reflex action it shows this: The ActionCable connection is not open! this.isActionCableConnectionOpen() must return true before calling this.stimulate(). I'm developing the app in a Vagrant box since i'm in a Windows machine, perhaps the problem is in there, but i'm not sure. Please help, i have no clue about how to solve this. Thanks.

Related

Access env['warden'].user from ApplicationCable is nil

So I am trying to implement dervise-jwt in a Rails 5.2 application. My login is working properly, and the tokens are being created. That being said, I want to access that same current_user from my ActionCable. Following some online tutorials, my app/channels/application_cable/connection.rb looks like this:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private
def find_verified_user
if user == env['warden'].user
user
else
reject_unauthorized_connection
end
end
end
end
When I debug the code, env['warden'].user is nil. What do I have to do to get the current_user accessible via warden?

Using ActionCable with multiple identification methods

I develop a Ruby on Rails 5.1 application using ActionCable. User authentification via Devise works fine for several channels. Now, I want to add a second type of channels which does not require any user authentification. More precisely, I would like to enable anonymous website visitors to chat with support staff.
My current implementation of ApplicationCable::Connection for authenticated users looks like this:
# 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
user = User.find_by(id: cookies.signed['user.id'])
return user if user
fail 'User needs to be authenticated.'
end
end
end
Anonymous users will be identified by some random UUID (SecureRandom.urlsafe_base64).
Question:
How do I best add this new type of channels? Could I add a boolean flag require_authentification somewhere, override it in my inherited channel class for anonymous communication, and switch the identification method in Connection depending on this attribute? Or would I rather have to implement a completely new module, say AnonymousApplicationCable?
Hi I came into the same problem, after looking at your solution in rails github comment, I assume it is better to create the token and keep the logic in the connect method.
So what I do was just utillize the the warden checking and if it is nil just create the anonymous token and otherwise. For this to work, I need to declare 2 identifier :uuid and :current_user
class Connection < ActionCable::Connection::Base
identified_by :current_user, :uuid
def connect
if !env['warden'].user
self.uuid = SecureRandom.urlsafe_base64
else
self.current_user = find_verified_user
end
end
protected
def find_verified_user # this checks whether a user is authenticated with devise
if verified_user = env['warden'].user
verified_user
else
reject_unauthorized_connection
end
end
end

Making current_user available in ActionCable channels using bcrypt

I'm trying to make current_user available in my channel actions.. This is the first time I'm diving into ActionCable and the tutorial I'm following is using Devise and I am not..
In channels/application_cable/connection.rb. They have
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 # this checks whether a user is authenticated with devise
if verified_user = env['warden'].user
verified_user
else
reject_unauthorized_connection
end
end
end
end
I'm having trouble with the protected method. I'm not sure how to verify the user from this file using bcrypt.. Also the directory this file is under seems to be likened to helpers for ActionCable, am I correct in thinking this?

env['warden'] not working with Rails 5

Im following this guide to create Chatting feature with a use of Websockets.
https://www.sitepoint.com/rails-and-actioncable-adding-advanced-features/
Im stuck with a problem that env['warden'].user is retuning nothing even when Im loggined to the app with standard Devise form.
And if I use another method (which is commented now) - it return wrong user
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 # this checks whether a user is authenticated with devise
verified_user = env['warden'].user
if verified_user
verified_user
else
reject_unauthorized_connection
end
end
# def find_verified_user
# user_id = request.headers['HTTP_AUTHORIZATION']
# if verified_user = User.find_by(user_id)
# verified_user
# else
# reject_unauthorized_connection
# end
# end
end
end
Logs says
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2017-04-06 17:40:17 +0300
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
An unauthorized connection attempt was rejected
Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
I had the same problem, but it turned out my issue was because I had two Devise models: User and Customer, since User was used for admin stuff, Customer was set to Warden's default_scope. So to access the user scope, I had to do the following
env['warden'].user(:user)
The symbol at the end defines the scope to use.
Here's where I found information on Warden's Scoped Users: https://github.com/wardencommunity/warden/wiki/Scopes#scoped-user-access
I found solution on this article
https://rubytutorial.io/actioncable-devise-authentication/
Im not sure how it works, but it does the deal. How it would help for people with similar problem.
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
verified_user = User.find_by(id: cookies.signed['user.id'])
if verified_user && cookies.signed['user.expires_at'] > Time.now
verified_user
else
reject_unauthorized_connection
end
end
end
end
And I also created /config/initializers/warden_hooks.rb file
Warden::Manager.after_set_user do |user,auth,opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = user.id
auth.cookies.signed["#{scope}.expires_at"] = 60.minutes.from_now
end
Warden::Manager.before_logout do |user, auth, opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = nil
auth.cookies.signed["#{scope}.expires_at"] = nil
end
Using Devise for authentication, I just ran into the same problem and the accepted answer didn't help me. My mistake was in my routes.rb file.
I placed: mount ActionCable.server, at: '/cable'
inside the authenticate :user do block. Moving it into the: Rails.application.routes.draw do block resolved the issue for me.

Rails 5 ActionCable error description and rejected connection

I made a basic chat with ActionCable authenticated with devise.
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 # this checks whether a user is authenticated with devise
if verified_user = env['warden'].user
verified_user
else
reject_unauthorized_connection
end
end
end
end
But when the user has an open chat and it rejects the connection (because the users have logged out), I need to show a login screen.
The problem is that on the frontend I can't get the reason for the disconnection.
How can I send reject with params, like "unauthorized"?
def find_verified_user # this checks whether a user is authenticated with devise
if verified_user = env['warden'].user
verified_user
else
message = "The user is not found. Connection rejected."
logger.add_tags 'ActionCable', message # to console
self.transmit error: message # this is what you wanted
reject_unauthorized_connection
end
end
See also: How to terminate subscription to an actioncable channel from server?

Resources