I have a rails API app using devise_token_auth and in my tests (minitest) I need to send POST requests and signed in users so I used the sign_in method but it doesn't seem to be doing anything.
This is my test file
require 'test_helper'
class ActivityControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
test "post activity" do
sign_in users('one')
post '/activity/', params: {
original_activity_log_file: fixture_file_upload('files/test_gpx.gpx', 'application/gpx'),
title: 'test activity',
description: 'test description'
}
assert_response 200
end
end
And this is the result when run
Expected response to be a <200: OK>, but was a <401: Unauthorized>
Response body: {"errors":["You need to sign in or sign up before continuing."]}.
Expected: 200
Actual: 401
What could be going on here to cause the test to fail?
You're using devise_token_auth in that case plain devise helpers doesn't work, you need perform plain authorization with post request. Here is an example:
# make a request in order to get tokens
post(
api_user_session_path,
params: {
email: "foo#bar.com",
password: "password"
}.to_json,
headers: {
'Content-Type' => 'application/json',
'Accept' => 'application/json'
}
)
# fetch data from 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
}
new_client = users('one')
get(
api_find_client_by_name_path(new_client.name),
headers: auth_params
)
assert_response :ok
rails 5.0.0.1
rspec 3.5
I have inherited a code base. I am busy writing integration tests to tie down the app functionality before I consider refactoring.
I have the following lines in a controller concern before_action. It seems to read the request body. The json value here is used to extract an identifier used to authenticate the request.
request.body.rewind
body = request.body.read
json = JSON.parse(body) unless body.empty?
I need to test that the authentication happens correctly.
How can I set the request.body for a GET request spec?
I think you should be able to do this via the request env RAW_POST_DATA
get root_path, {}, 'RAW_POST_DATA' => 'raw json string'
request.raw_post # "raw json string"
See:
How to send raw post data in a Rails functional test?
https://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec
#rails_post_5
require "rails_helper"
RSpec.describe "Widget management", :type => :request do
it "creates a Widget and redirects to the Widget's page" do
headers = { "CONTENT_TYPE" => "application/json" }
post "/widgets", :params => '{ "widget": { "name":"My Widget" } }', :headers => headers
expect(response).to redirect_to(assigns(:widget))
end
end
or just
post "/widgets", params: '{ "widget": { "name":"My Widget" } }'
I'm trying to add an authorization token to an RSpec get JSON API test in Rails. But everything tried so far results in an error. The issue seems that the token is not being properly passed in the request.
expected the response to have a success status code (2xx) but it was 401
Current code:
Project_spec.rb (tests)
before do
#project = create(:project, :key => "123")
get '/api/v1/projects/1', {:Authorization => "Token 123"}, format: :json
end
it "returns correct status" do
expect( response ).to have_http_status(:success)
end
ProjectsController.rb
before_filter :restrict_access, :except => :create
def show
#project = Project.find(params[:id])
render json: #project
end
def restrict_access
authenticate_or_request_with_http_token do |token, options|
Project.exists?(key: token)
end
end
Based on a few recommended solution found online, I've tried
get '/api/v1/projects/1', format: :json, token: "123"
get '/api/v1/projects/1', { 'HTTP-AUTHORIZATION' => 'Token "123"' }, format: :json
get '/api/v1/projects/1', {:Authorization => "Token 123"}, format: :json
But nothing seems to successfully pass the authorization token.
Note: Style in #3 works when posting from an external application, but not in the RSpec test itself.
Does anyone have experience with this and can share some direction?
Use it like:
get '/api/v1/projects/1', {}, { Authorization: "Token 123"}
get is the http method, {} an empty params, { :Authorization => "Token 123"} headers
get(path, parameters = nil, headers_or_env = nil)
Documentation
Other way:
before do
# some code here
#request.env['Authorization'] = "Token 123"
get '/api/v1/projects/1', format: :json
end
It worked when I just tried like this
get '/api/v1/projects/1', as: :json, headers: {:Authorization => "Token 123"}
When I testing the procedure of requesting an access token, which is part of authorize flow, from Doorkeeper gem at localhost side through RSpec with Ruby on Rails, Devise, Grape and Wine_bouncer, RSpec always receives a 401 response from Doorkeeper, whose error description says Invalid-grant: The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
I want to know how to solve this problem. Please help me, thank you.
The followings are my test environments and code:
Ruby 2.3.0
Rails 4.2.5.1
Doorkeeper 3.1.0
Devise 3.5.5
RSpec-core 3.4.2, RSpec-support 3.4.1, RSpec-mocks 3.4.1, RSpec-rails 3.4.1, RSpec-expectations 3.4.0
Wine_bouncer 0.5.1
Doorkeeper configuration at config/initializers/doorkeeper.rb
Doorkeeper.configure do
orm :active_record
resource_owner_authenticator do
session[:user_return_to] = request.fullpath
current_user || redirect_to(new_user_session_url)
end
authorization_code_expires_in 20.minutes
access_token_expires_in 30.days
enable_application_owner :confirmation => false
default_scopes :public, :description => "Access public data."
optional_scopes :write, :description => "Update your data."
optional_scopes :admin, :description => "Do admin things."
access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
grant_flows %w(authorization_code client_credentials implicit)
skip_authorization do
true
end
end
spec/requests/api/v1/specifiedvegetables_spec.rb
describe SpecifiedVegetables do
describe 'OAuth client requests the grant' do
context 'When a REST client sends a request for getting the grant' do
before(:all) do
post "http://localhost:3000/users/sign_in?user[email]=test%40test123%2Ecom&user[password]=12345678" # Log in the devise
#app = Doorkeeper::Application.new :name => "rspectest-107", :redirect_uri => "https://localhost:3000/api/v1/specified_vegetables/", :scopes => "public"
#app.owner = User.last
#app.save! # Create OAuth client into database.
#authorize_code = String.new # Use later for getting authorize grant code
end
it 'should getting response code 302 for requesting authorization code.' do
query_url = "http://localhost:3000/oauth/authorize"
parameters = { "response_type" => "code", "client_id" => #app.owner.oauth_applications.last.uid, "redirect_uri" => "https://localhost:3000/api/v1/specified_vegetables/", "scope" => "public"}
headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
get query_url, parameters, headers # Send request for getting authorize grant code
expect(response.status).to eq(302)
authorize_code_param = Rack::Utils.parse_query(URI.parse(response.location).query)
#authorize_code << authorize_code_param['code'] # Get the authorize grant code
end
it 'should get response code 302 for requesting access token.' do
query_url = "http://localhost:3000/oauth/token"
parameters = {"grant_type" => "authorization_code", "code" => #authorize_code, "client_id" => #app.owner.oauth_applications.last.uid, "redirect_uri" => "https://localhost:3000/api/v1/specified_vegetables/"}
headers = {'Content-Type' => 'application/x-www-form-urlencoded', "Authorization" => "Basic " + Base64.urlsafe_encode64(#app.owner.oauth_applications.last.uid + ":" + #app.owner.oauth_applications.last.secret, :padding => false)}
post query_url, parameters, headers # Send request for getting access token
expect(response).to eq(200) # **Receive the Error response**
end
after(:all) do
#app.destroy # After running all test cases, destroy this OAuth client application.
end
end
end
end
Error response after running RSpec command at root directory of rails app.
rspec spec/requests/api/v1/specifiedvegetables_spec.rb
expected: 200
got: #, #stream=#, #buf=["{\"error\":\"invalid_grant\",\"error_description\":\"The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.\"}"], #closed=false>, #header={"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1; mode=block", "X-Content-Type-Options"=>"nosniff", "Cache-Control"=>"no-store", "Pragma"=>"no-cache", "Content-Type"=>"application/json; charset=utf-8", "WWW-Authenticate"=>"Bearer realm=\"Doorkeeper\", error=\"invalid_grant\", error_description=\"The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.\"", "X-Request-Id"=>"97099b43-3456-4396-b9d9-cf744ec38ea6", "X-Runtime"=>"0.006156", "Content-Length"=>"213"}, #status=401, #sending_file=false, #blank=false, #cv=#, #cond=#>, #committed=false, #sending=false, #sent=false, #content_type=#, #charset="utf-8", #cache_control={:extras=>["no-store"]}, #etag=nil>
20160306 PM10:39 UTC+8 update:
If I try to get access token through Postman software(get it from Chrome store), I can get it as I expect. But trying to get it through rspec, I can't acquire it.
I have used the modified ruby-type code in RSpec, which are from Postman generate function, I still can't get access token successfully. I think this difference is weird.
20160307 PM03:10 Update:
The related file has been put at github.com . I've gotten an access token through Postman software but I still don't understand why I can acquire access token through RSpec although have been tried many other codes to get. It's still unsolved by RSpec.
I've never used Doorkeeper gem, I use Devise Token Auth. I checked the documentation though and found this. You can stub the :doorkeeper_token method to test protected methods.
let(:token) { double :acceptable? => true }
before do
controller.stub(:doorkeeper_token) { token }
# allow(controller).to receive(:doorkeeper_token) {token} # => RSpec 3
end
https://github.com/doorkeeper-gem/doorkeeper/wiki/Testing-protected-controllers
Sorry, I think I have misunderstood about it-statement usage of RSpec test cases after trying many things.
Right meaning of it-statement in RSpec: Every it-statement is independent between each other even if they're under same context- or describe-statement.
So if I combine two it-statements of requesting authorization code and requesting access token together, I will be at halfway towards passing test with RSpec.
The other thing I need to fix is to use right encoding of Authorization at HTTP header for acquiring access token. Change from Base64.urlsafe_encode64 to Base64.strict_encode64, please.
Following are the right code of spec/requests/api/v1/specifiedvegetables_spec.rb:
require 'rails_helper'
describe SpecifiedVegetables do
describe 'OAuth client requests the grant' do
context 'When a REST client sends a request for getting the grant' do
before(:all) do
post "http://localhost:3000/users/sign_in?user[email]=test%40test123%2Ecom&user[password]=12345678"
expect(response.status).to eq(302)
#app = Doorkeeper::Application.new :name => 'rspectest-107', :redirect_uri => 'https://localhost:3000/api/v1/specified_vegetables/', :scopes => 'public'
#app.owner = User.last
#app.save!
#authorize_code = String.new
end
it 'should getting response code 302 for requesting authorization code and access token' do
query_url = "http://localhost:3000/oauth/authorize"
parameters = { "response_type" => "code", "client_id" => #app.owner.oauth_applications.last.uid, "redirect_uri" => "https://localhost:3000/api/v1/specified_vegetables/", "scope" => "public"}
headers = {"content-type" => "application/x-www-form-urlencoded"}
get query_url, parameters, headers
expect(response.status).to eq(302)
authorize_code_param = Rack::Utils.parse_query(URI.parse(response.location).query)
#authorize_code << authorize_code_param['code']
# above are acquiring authorize code
# The following are acquiring access token
query_url = "http://localhost:3000/oauth/token"
parameters = { "grant_type" => "authorization_code", "code" => #authorize_code , "client_id" => #app.owner.oauth_applications.last.uid, "redirect_uri" => "https://localhost:3000/api/v1/specified_vegetables/"}
headers = {"content-type" => "application/x-www-form-urlencoded", "authorization" => "Basic " + Base64.strict_encode64(#app.owner.oauth_applications.last.uid + ":" + #app.owner.oauth_applications.last.secret), "cache-control" => "no-cache"}
post query_url, parameters, headers
expect(response.status).to eq(200) # Here, we get status 200 because the response has access token.
end
after(:all) do
#app.destroy # Destroy the oauth client, but doesn't purge related access token for this rspec request.
end
end
end
end
I'm trying to use a Doorkeeper gem to protect my API.
My code looks like this:
initializers/doorkeeper.rb
Doorkeeper.configure do
resource_owner_authenticator do
current_user || warden.authenticate!(:scope => :user)
end
default_scopes :public # if no scope was requested, this will be the default
optional_scopes :admin, :write
enable_application_owner :confirmation => false
end
Here are commands I'm using to connect to my API:
RestClient.post 'http://localhost:3000/oauth/token', {
grant_type: 'client_credentials',
client_id: '26b8e5c92367d703ad35a2fc16b14dc93327a15798068ccba473aa2e3d897883',
client_secret: 'b16079915cdc20b5373f1601e31cece5a84274f772cfd89aec12c90fd110775e'
}
... and ...
RestClient.get 'http://localhost:3000/api/v1/videos', { 'Authorization' => 'Bearer <token_from_previous_request>' }
Which works fine but my problem is, that the returned Token object has an empty resource_owner_id param (this column is not being populated in the DB on token creation). Have you any idea what am i doing wrong? I've been following those tutorials:
https://github.com/applicake/doorkeeper/wiki/Associate-users-to-OAuth-applications-%28ownership%29
https://github.com/applicake/doorkeeper/wiki/Client-Credentials-flow
The client credentials flow "is not associated with a resource owner" (https://github.com/applicake/doorkeeper/wiki/Client-Credentials-flow), so I think it is right that the resource_owner_id is not set.