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

后端 未结 5 1646
故里飘歌
故里飘歌 2020-12-15 20:57

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.
相关标签:
5条回答
  • 2020-12-15 21:27

    Well, in theory:

    • You have access to the cookies in the ActiveCable::Connection class.
    • You can set and receive cookies.signed and cookies.encrypted
    • Both the application and ActionCable share same configuration, therefore they share same `secret_key_base'

    So, if you know the name of your session cookie (somehow obvious, let it be called "_session"), you can simply receive the data in it by:

    cookies.encrypted['_session']
    

    So, you should be able to do something like:

    user_id = cookies.encrypted['_session']['user_id']
    

    This depends on do you use cookie store for the session and on the exact authentication approach, but in any case the data you need should be there.

    I found this approach more convenient as the session is already managed by the authentication solution you use and you more likely don't need to care about things like cookie expiration and duplication of the authentication logic.

    Here is more complete example:

    module ApplicationCable
      class Connection < ActionCable::Connection::Base
        identified_by :current_user
    
        def connect
          session = cookies.encrypted['_session']
          user_id = session['user_id'] if session.present?
    
          self.current_user = (user_id.present? && User.find_by(id: user_id))
    
          reject_unauthorized_connection unless current_user
        end
      end
    end
    
    
    0 讨论(0)
  • 2020-12-15 21:28

    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.

    0 讨论(0)
  • 2020-12-15 21:32

    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.

    0 讨论(0)
  • 2020-12-15 21:35

    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
    
    
    0 讨论(0)
  • 2020-12-15 21:39

    For Rails 5 API mode:

    application_controller.rb

    class ApplicationController < ActionController::API
       include ActionController::Cookies
       ...
       token = request.headers["Authorization"].to_s
       user = User.find_by(authentication_token: token)
       cookies.signed[:user_id] = user.try(:id)
    

    connection.rb

    class Connection < ActionCable::Connection::Base
       include ActionController::Cookies
       ...
       if cookies.signed[:user_id] && current_user = User.where(id: cookies.signed[:user_id]).last
         current_user
       else
         reject_unauthorized_connection
       end
    

    config/application.rb

    config.middleware.use ActionDispatch::Cookies
    
    0 讨论(0)
提交回复
热议问题