mgomes / api_auth

HMAC authentication for Rails and HTTP Clients
MIT License
480 stars 147 forks source link

Writing rails controller tests for api-auth #57

Closed MichaelSp closed 9 years ago

MichaelSp commented 9 years ago

I'm trying to write test for an authenticated API-Access like so

test 'api access with valid request' do
      @user = users(:one)
      @request = ApiAuth.sign!(@request, @user.id, @user.secret_access_token)
      get :index
      assert_response :success
end

The server side validation looks like this

module Api
  class ApplicationController < ActionController::Base
    respond_to :json
    before_action :api_authenticate

     def api_authenticate
      user_id = ApiAuth.access_id(request)
      raise 'id not found' if user_id.blank?
      @current_user = User.find(user_id)
      raise 'user not found' if @current_user.blank?
      raise 'token not valid' unless ApiAuth.authentic?(request, @current_user.secret_access_token)
     rescue Exception => e
       logger.warn "API-Call failed: #{e.message}"
       head :unauthorized
    end
  end
end

This simple test case does not work. It always fails with Minitest::Assertion: Expected response to be a <success>, but was <401>. I was able to track this down to the following issue: The @request gets signed without the PATH-INFO header. And it gets authenticated with the PATH-INFO. The PATH-INFO is set inside of get :index. So there is no way to sign the request after PATH-INFO has been set.

What do you think? Is there a way to get the test running?

kjg commented 9 years ago

Checkout the discussion here: https://github.com/mgomes/api_auth/issues/50

Brief summary: I recommend not testing API Auth in a rails test and just stubbing it out with controller.stub(api_authenticate: true) or controller.class.skip_before_filter :api_authenticate

However you can get the test running by mocking fullpath on the TestRequest object. The rspec way would be

allow_any_instance_of(ActionDispatch::TestRequest).to receive(:fullpath).and_return(controller_path)

also make sure the content type is set with something like request.env["HTTP_ACCEPT"] = "application/json"

MichaelSp commented 9 years ago

Wow thanks for your immediate reply. I agree, the authentication should be tested inside of the gem. So there is no reason to test this again. I'll stub it out as you suggested. This topic seems like something for the Readme.

Here is my MiniTest-Spec code to stub the authentication:

before do
  @controller.expects(:api_authenticate)
end

Oh by the way. Returning false from a before_action/before_filter in rails 4 won't stop the rendering of the action. That's why I raise/rescued and rendered head :unauthorized. See http://stackoverflow.com/questions/20623616/what-does-before-action-returning-false-do-in-rails-4

evanjmg commented 8 years ago

how can I test whether it is on? I put mine in a before_action in my api_controller.

MichaelSp commented 8 years ago

I don't understand "whether it is on". What is "it"?

evanjmg commented 8 years ago

how can I test if the before_action is running? Sometimes I comment it out for postman/curl requests; it'd be nice to test whether api_auth is actually being used in my api.

MichaelSp commented 8 years ago

You can test if there's a Authorization header set in your request.