Ruby-on-rails – the proper way to grab Authorization header from controller request object

rspecruby-on-railsruby-on-rails-3

I have two RSpec tests, a controller spec and a request spec, where I am making a GET request to the index action of the same controller. In both specs I am sending an Authorization header that contains an Oauth2 bearer token.

The problem I'm having is that depending on the type of spec, the header is stored on a different property of the request object. In the case of the request spec, it is available at request.env["Authorization"] and in the case of the controller spec, it is available at request.session["Authorization"].

Why is "Authorization" stored in different places for different types of specs? Is there somewhere I can find it for both specs?

This bearer_token method is in the parent controller class where I'm grabbing the token from the header:

Works with env in the request specs:

def bearer_token
  pattern = /^Bearer /
  header  = request.env["Authorization"] # <= env
  header.gsub(pattern, '') if header && header.match(pattern)
end

Works with session in the controller specs:

def bearer_token
  pattern = /^Bearer /
  header  = request.session["Authorization"] # <= session
  header.gsub(pattern, '') if header && header.match(pattern)
end

Here is my request spec:

describe '' do
  let(:user) { Fabricate(:user) }

  describe 'accessing content with valid token' do
    let(:token) { OauthToken.create(user: user) }
    let(:auth_headers) { {
      'Authorization' => "Bearer #{token.access_token}",
      'HTTPS' => 'on'
    } }
    before { get api_v2_cats_path, {}, auth_headers }
    specify { response.status.should == 200 }
  end
end

Here is my controller spec

describe Api::V2::CatsController do
  let(:user) { Fabricate(:user) }

  describe ".index" do
    let(:token) { OauthToken.create(user: user) }
    let(:auth_headers) { {
      'Authorization' => "Bearer #{token.access_token}",
      'HTTPS' => 'on'
    } }

    it "should be valid" do
      get :index, { format: :json, page_size: 1 }, auth_headers
      @json = JSON.parse(response.body)
      @json.should_not be_nil
    end
  end
end

Best Answer

I assumed that the API would be the same for the get method between a request and controller spec. In the controller spec, the third argument is a hash of sessions variables, not header variables. You can set the headers directly on the @request object like so:

describe Api::V2::CatsController do
  let(:user) { Fabricate(:user) }

  describe ".index" do
    let(:token) { OauthToken.create(user: user) }
    let(:auth_headers) { {
      'Authorization' => "Bearer #{token.access_token}",
      'HTTPS' => 'on'
    } }

    before do
      @request.env.merge!(auth_headers)
    end

    it "should be valid" do
      get :index, { format: :json, page_size: 1 }
      @json = JSON.parse(response.body)
      @json.should_not be_nil
    end
  end
end

Then the correct way to get the authorization header is using:

def bearer_token
  pattern = /^Bearer /
  header  = request.env["Authorization"] # <= env
  header.gsub(pattern, '') if header && header.match(pattern)
end
Related Topic