ruby-on-railsactioncabledoorkeeper

Secure Action cable with Doorkeeper Authorisation


I am working on ActionCable and implemented Doorkeeper Authorization in my rails application.

I want to implement authenticate my client with Doorkeeper::AccessToken with ActionCable

Here is how I authenticated right now:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user
    identified_by :room_id

    def connect
      self.current_user = find_verified_user
      self.room_id = @user.ac_channel_room
    end

    def disconnect
      # When user will disconnect action cable, this method call will be executed.
    end

    private

    def find_verified_user 
      check_access_token
      @user = User.find_by_id(@resource_owner_id) if @resource_owner_id
      reject_unauthorized_connection unless @user
    end

    def check_access_token
      # Check provided token is valid or not
      params = request.query_parameters
      @access_token ||= Doorkeeper::AccessToken.by_token(params[:access_token])
      @resource_owner_id = @access_token&.resource_owner_id
    end
  end
end

Problem is this is allowing experied access tokens as well.

Please help!


Solution

  • Your question will allow action cable connection with expired Doorkeeper::AccessToken objects.

    Here is the solution:

    module ApplicationCable
      class Connection < ActionCable::Connection::Base
        identified_by :current_user
    
        def connect
          self.current_user = authenticate!
        end
    
        protected
    
        def authenticate!
          reject_unauthorized_connection unless doorkeeper_token&.acceptable?(@_doorkeeper_scopes)
    
          # this will still allow expired tokens
          # you will need to check if token is valid with something like
          # doorkeeper_token&.acceptable?(@_doorkeeper_scopes)
    
          user = User.find_by(id: doorkeeper_token.try(:resource_owner_id))
    
          user || reject_unauthorized_connection
        end
    
        def doorkeeper_token
          ::Doorkeeper.authenticate(request)
        end
      end
    end
    
    # ...
    
    class SomeChannel < ApplicationCable::Channel
      def subscribed
         reject unless current_user
         stream_from 'some'
      end
    end