Rails Action Cable: How to authorize incoming connection?
Asked Answered
B

3

5

I am trying to implement authorization on the websocket connection (rails 5.2.1)

Following the rubyonrails guideline, I have created the connection.rb as follows:

# 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

Unfortunately, all the websocket connection requests are rejected. (I have figured out that cookies.encrypted[:user_id] is returning nil.)

Started GET "/cable" for ::1 at 2018-10-07 21:33:46 +0300
Started GET "/cable/" [WebSocket] for ::1 at 2018-10-07 21:33:46 +0300
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
  User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."is_active" = $1 AND "users"."id" IS NULL LIMIT $2  [["is_active", true], ["LIMIT", 1]]
  ↳ app/channels/application_cable/connection.rb:12
An unauthorized connection attempt was rejected
Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Finished "/cable/" [WebSocket] for ::1 at 2018-10-07 21:33:46 +0300
Finished "/cable/" [WebSocket] for ::1 at 2018-10-07 21:33:46 +0300

Would you please guide me how I can access current user info within app/channels/application_cable/connection.rb?

Booker answered 7/10, 2018 at 18:43 Comment(0)
W
10

In order to get current_user from Devise, you need to change your connection.rb as follows:

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
Waxler answered 7/10, 2018 at 19:25 Comment(7)
I have tried cookies.signed instead of encrypted but nothing has changed. Also I enabled debugger on the "find_verified_user" method and it seems cookies.signed[:user_id] is nill (although user is authenticated in rails): (byebug) cookies.signed[:user_id] nilBooker
By the way, in order to use Action Cable, I have recently upgraded this app from Rails 4.2.10 to Rails 5.2.1 . I just wanted you to know since this is not a fresh rails 5.2.1 app and I might be missing some other configs which may be required?Booker
Are you using devise as authentication solution or any other gem for this purpose?Waxler
I am using deviseBooker
Well then I think you need to update your connection.rb file so it maps with Devise. E.g. if verified_user = env['warden'].user in the case of verifying the user. Check the following link to see how to setup ActionCable when using Devise: sitepoint.com/…Waxler
Thanks John.. It solved the problem!! :) Can you pls move the last comment into answer so that I can accept it officially?Booker
Great! Yep, in a second :)Waxler
M
0

I faced the exact problem. The way I solved it was by adding a cookie when the user signed in, through SessionsController < Devise::SessionsController. The code looks like this:

class Users::SessionsController < Devise::SessionsController
  # POST /resource/sign_in
  def create
    super
    cookies[:user_id] = current_user.id
  end
end

You can get the SessionsController from devise by running the code:

rails generate devise:controllers [scope]

In my case the scope was users.

Metry answered 18/3, 2020 at 10:12 Comment(1)
This is wrong, you should use session, because session is encrypted in Rails (via RACK), cookies are not. Also, session cookies are not available to browser.Oreilly
K
0

Rails 6:

cookies.encrypted[:user_id]

Rails 5:

cookies.signed[:user_id]
Kirsti answered 3/6, 2020 at 15:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.