Testing

In order to authorize a request when testing your API you will need to pass the four headers through with your request, the easiest way to gain appropriate values for those headers is to use resource.create_new_auth_token e.g.

  request.headers.merge! resource.create_new_auth_token
  get '/api/authenticated_resource'
  # success

Check #75 if you have any problem or doubt.

Testing with Rspec

(a) General Request Specs

Below are some generic examples which may assist in helping you devise (pun intended) your own tests:

# spec/requests/authentication_test_spec.rb

require 'rails_helper'
include ActionController::RespondWith

# The authentication header looks something like this:
# {"access-token"=>"abcd1dMVlvW2BT67xIAS_A", "token-type"=>"Bearer", "client"=>"LSJEVZ7Pq6DX5LXvOWMq1w", "expiry"=>"1519086891", "uid"=>"darnell@konopelski.info"}

describe 'Whether access is ocurring properly', type: :request do
  before(:each) do
    @current_user = FactoryBot.create(:user)
    @client = FactoryBot.create(:client)
  end

  context 'context: general authentication via API, ' do
    it "doesn't give you anything if you don't log in" do
      get api_client_path(@client)
      expect(response.status).to eq(401)
    end

    it 'gives you an authentication code if you are an existing user and you satisfy the password' do
      login
      # puts "#{response.headers.inspect}"
      # puts "#{response.body.inspect}"
      expect(response.has_header?('access-token')).to eq(true)
    end

    it 'gives you a status 200 on signing in ' do
      login
      expect(response.status).to eq(200)
    end

    it 'first get a token, then access a restricted page' do
      login
      auth_params = get_auth_params_from_login_response_headers(response)
      new_client = FactoryBot.create(:client)
      get api_find_client_by_name_path(new_client.name), headers: auth_params
      expect(response).to have_http_status(:success)
    end

    it 'deny access to a restricted page with an incorrect token' do
      login
      auth_params = get_auth_params_from_login_response_headers(response).tap do |h|
        h.each do |k, _v|
          if k == 'access-token'
            h[k] = '123'
          end end
      end
      new_client = FactoryBot.create(:client)
      get api_find_client_by_name_path(new_client.name), headers: auth_params
      expect(response).not_to have_http_status(:success)
    end
  end

  RSpec.shared_examples 'use authentication tokens of different ages' do |token_age, http_status|
    let(:vary_authentication_age) { token_age }

    it 'uses the given parameter' do
      expect(vary_authentication_age(token_age)).to have_http_status(http_status)
    end

    def vary_authentication_age(token_age)
      login
      auth_params = get_auth_params_from_login_response_headers(response)
      new_client = FactoryBot.create(:client)
      get api_find_client_by_name_path(new_client.name), headers: auth_params
      expect(response).to have_http_status(:success)

      allow(Time).to receive(:now).and_return(Time.now + token_age)

      get api_find_client_by_name_path(new_client.name), headers: auth_params
      response
    end
  end

  context 'test access tokens of varying ages' do
    include_examples 'use authentication tokens of different ages', 2.days, :success
    include_examples 'use authentication tokens of different ages', 5.years, :unauthorized
  end

  def login
    post api_user_session_path, params:  { email: @current_user.email, password: 'password' }.to_json, headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
  end

  def get_auth_params_from_login_response_headers(response)
    client = response.headers['client']
    token = response.headers['access-token']
    expiry = response.headers['expiry']
    token_type = response.headers['token-type']
    uid = response.headers['uid']

    auth_params = {
      'access-token' => token,
      'client' => client,
      'uid' => uid,
      'expiry' => expiry,
      'token-type' => token_type
    }
    auth_params
  end
end

(b) How to create an authorization header from Scratch

require 'rails_helper'
include ActionController::RespondWith

def create_auth_header_from_scratch
  # You need to set up factory bot to use this method
  @current_user = FactoryBot.create(:user)

  # create token
  token = DeviseTokenAuth::TokenFactory.create

  # store client + token in user's token hash
  @current_user.tokens[token.client] = {
    token:  token.token_hash,
    expiry: token.expiry
  }

  # Now we have to pretend like an API user has already logged in.
  # (When the user actually logs in, the server will send the user
  # - assuming that the user has  correctly and successfully logged in
  # - four auth headers. We are to then use these headers to access
  # things which are typically restricted
  # The following assumes that the user has received those headers
  # and that they are then using those headers to make a request

  new_auth_header = @current_user.build_auth_headers(token.token, token.client)

  puts 'This is the new auth header'
  puts new_auth_header.to_s

  # update response with the header that will be required by the next request
  puts response.headers.merge!(new_auth_header).to_s
end

Further Examples of Request Specs

  • https://gist.github.com/blaze182/3a59a6af8c6a7aaff7bf5f8078a5f2b6

  • https://gist.github.com/niinyarko/f146f24a50125d55396f63043a2696e7

Last updated