env['warden'] not working with Rails 5 - ruby-on-rails

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.

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?

Error in ActionCable with Apartment gem and Stimulus Reflex

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.

How to fix "Could not execute command from ({"command"=>"subscribe", "identifier"=>"{\"channel\":\"ConversationChannel\"}"}) "

I'm trying to get user to connect user to conversation_channel and I receive:
Could not execute command from ({"command"=>"subscribe", "identifier"=>" {\"channel\":\"ConversationChannel\"}"}) [NoMethodError - undefined method `id' for nil:NilClass]: /mnt/c/rails_apps/Picnotes/app/channels/conversation_channel.rb:6:in `subscribed' |
I tried hardcoding current_user.id to 1 or 2 and I got:
Started GET "/cable" for 127.0.0.1 at 2019-01-22 22:06:36 -0800
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2019-01-22 22:06:36 -0800
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
ConversationChannel is transmitting the subscription confirmation
ConversationChannel is streaming from conversations-1
My code is below:
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
class ConversationChannel < ApplicationCable::Channel
# Create a unique channel for each user.
...
def subscribed
stream_from "conversations-#{current_user.id}"
end
# Removes all connected connections.
def unsubscribed
stop_all_streams
end
...
end
end
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = current_user
end
end
end
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :initiate_instance_variables
...
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
helper_method :current_user
...
end
I expect #{current_user.id} to be the user id (constant: 1 or 2), but the object is always nil.
According the rails guides for ActionCable you need to get your user id from your cookies
# 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
private
def find_verified_user
if verified_user = User.find_by(id: cookies.encrypted[:user_id])
verified_user
else
reject_unauthorized_connection
end
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?

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