Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get current_user in ActionCable rails-5-api app?

Why am I not able to retrieve current_user inside my channel or how should I retrieve current_user?

What do I use?

  • Rails 5.0.1 --api (I do NOT have any views NOR use coffee)
  • I use react-native app to test this (Works fine WITHOUT authorization)
  • I do NOT use devise for auth (I use JWT instead using Knock, so no cookies)

Trying to get current_user inside my ActionCable channel as described in rubydoc.info

The code looks like

class MessageChannel < ApplicationCable::Channel
  identified_by :current_user

  def subscribed
    stream_from 'message_' + find_current_user_privileges
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  protected

  def find_current_user_privileges
    if current_user.has_role? :admin
      'admin'
    else
      'user_' + current_user.id
    end
  end

end

And running it, I get this error:

[NoMethodError - undefined method `identified_by' for MessageChannel:Class]

And if I remove identified_by :current_user, I get

[NameError - undefined local variable or method `current_user' for #<MessageChannel:0x7ace398>]
like image 294
Saravanabalagi Ramachandran Avatar asked Feb 25 '17 04:02

Saravanabalagi Ramachandran


3 Answers

If you see the doc you provided, you will know that identified_by is not a method for a Channel instance. It is a method for Actioncable::Connection. From Rails guide for Actioncable Overview, this is how a Connection class looks like:

#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 current_user = User.find_by(id: cookies.signed[:user_id])
          current_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

As you can see, current_user is not available here. Instead, you have to create a current_user here in connection.

The websocket server doesn't have a session, but it can read the same cookies as the main app. So I guess, you need to save cookie after authentication.

like image 169
Sajan Avatar answered Oct 11 '22 13:10

Sajan


if you are using devise gems in rails, Please replace this function:

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

I hope this will help you.

like image 31
Sochetra Nov Avatar answered Oct 11 '22 13:10

Sochetra Nov


After setting self.current_user in ApplicationCable::Connection it become available in the channel instances. So you can set up your authentication like Sajan wrote and just use current_user in MessageChannel

For example this code worked for me

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

    def connect
      self.verified_user = find_verified_user
    end

    private

    def current_user
      jwt = cookies.signed[:jwt]
      return unless jwt

      decoded = JwtDecodingService.new(jwt).decrypt!
      @current_user ||= User.find(decoded['sub']['user_id'])
    end

    def find_verified_user
      current_user || reject_unauthorized_connection
    end
  end
end

class NextFeaturedPlaylistChannel < ApplicationCable::Channel
  def subscribed
    stream_from "next_featured_playlist_#{verified_user.id}"
  end
end

like image 21
Axife Avatar answered Oct 11 '22 13:10

Axife