Knock: Implementing "become user"

Created on 2 Aug 2017  路  2Comments  路  Source: nsarno/knock

Curious if anybody has implemented a "become user" feature (or this is even possible with JWT)? It is very helpful for support purposes to see the exact screens of a given user's account.

I've done this before with devise. Anybody tried the same with knock / have ideas of how to do it?

My first thinking would be to override from_token_request and from_token_payload. Logic would be something like:

  1. Check the actual parameter user has the necessary privs (i.e. support role)
  2. Check for an additional parameter referencing a 'becomeuser'
  3. Return the 'becomeuser'

Most helpful comment

So, I ended up implementing this via a monkey patch (eww, I know). Basically I require a user to have a role of superadmin, at which point I'll allow authentication to happen against their account but then the entity that is loaded is the target user's account. Seems to work well so far.

Here is the code I used for future reference:

# lib/core_extentions/knock/auth_token_controller.rb
Knock::AuthTokenController.class_eval do
  private
    def authenticate
      # if "become" is present then auth as the core user (not as the entity)
      become = request.params["become"]
      if become
        u = User.find_by email: auth_params[:email], active: true, role: :superadmin
        unless u.present? && u.authenticate(auth_params[:password])
          raise Knock.not_found_exception_class
        end
        return true
      end

      unless entity.present? && entity.authenticate(auth_params[:password])
        raise Knock.not_found_exception_class
      end
    end
end
# models/user.rb
  def self.from_token_request request
    #  entity_class.find_by email: auth_params[:email]
    # Returns a valid user, `nil` or raise `Knock.not_found_exception_class_name`
    # e.g.
    email = request.params["auth"] && request.params["auth"]["email"]
    # check if a request has been made to "become" another user, if so the
    # user must have a role of 'superadmin'
    become = request.params["become"]
    if become
      u = self.find_by email: email, active: true, role: :superadmin
      email = become if u.present?
    end

    self.find_by email: email, active: true
  end

All 2 comments

So, I ended up implementing this via a monkey patch (eww, I know). Basically I require a user to have a role of superadmin, at which point I'll allow authentication to happen against their account but then the entity that is loaded is the target user's account. Seems to work well so far.

Here is the code I used for future reference:

# lib/core_extentions/knock/auth_token_controller.rb
Knock::AuthTokenController.class_eval do
  private
    def authenticate
      # if "become" is present then auth as the core user (not as the entity)
      become = request.params["become"]
      if become
        u = User.find_by email: auth_params[:email], active: true, role: :superadmin
        unless u.present? && u.authenticate(auth_params[:password])
          raise Knock.not_found_exception_class
        end
        return true
      end

      unless entity.present? && entity.authenticate(auth_params[:password])
        raise Knock.not_found_exception_class
      end
    end
end
# models/user.rb
  def self.from_token_request request
    #  entity_class.find_by email: auth_params[:email]
    # Returns a valid user, `nil` or raise `Knock.not_found_exception_class_name`
    # e.g.
    email = request.params["auth"] && request.params["auth"]["email"]
    # check if a request has been made to "become" another user, if so the
    # user must have a role of 'superadmin'
    become = request.params["become"]
    if become
      u = self.find_by email: email, active: true, role: :superadmin
      email = become if u.present?
    end

    self.find_by email: email, active: true
  end

Thanks, @krsyoung ! I'm surprised this doesn't have any more comments on it. It's a very helpful issue. If Knock was more presently-maintained I'd say it should be included in the main documentation..

Was this page helpful?
0 / 5 - 0 ratings