lynndylanhurley/devise_token_auth

View on GitHub
docs/usage/testing.md

Summary

Maintainability
Test Coverage
# 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.

```Ruby
  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:

```ruby
# 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

```ruby
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