【问题标题】:RSpec requests an access token from Doorkeeper and then always gets invalid_grant errorRSpec 从 Doorkeeper 请求访问令牌,然后总是收到 invalid_grant 错误
【发布时间】:2016-06-19 17:26:55
【问题描述】:

当我使用 Ruby on Rails、Devise、Grape 和 Wine_bouncer 从本地主机端的 Doorkeeper gem 通过 RSpec 测试请求访问令牌的过程时,这是授权流程的一部分,RSpec 总是收到来自 Doorkeeper 的 401 响应,其错误描述说 Invalid-grant:提供的授权授权无效、已过期、已撤销、与授权请求中使用的重定向 URI 不匹配,或已发布给另一个客户端.

我想知道如何解决这个问题。请帮帮我,谢谢。

以下是我的测试环境和代码:

  • Ruby 2.3.0
  • Rails 4.2.5.1
  • 看门人 3.1.0
  • 设计 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

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


错误响应在 rails 应用根目录运行 RSpec 命令后。

  • rspec spec/requests/api/v1/specifiedvegetables_spec.rb

预计:200
got:#,@stream=#,@buf=["{\"error\":\"invalid_grant\",\"error_description\":\"提供的授权授权无效,过期,撤销,不匹配授权请求中使用的重定向 URI,或已发布给另一个客户端。\"}"], @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=\"提供授权授权无效、已过期、已撤销、与授权请求中使用的重定向 URI 不匹配或已发布给另一个客户端。\"", "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 更新:

如果我尝试通过 Postman 软件获取访问令牌(从 Chrome 商店获取),我可以按预期获取。但是试图通过rspec获得它,我无法获得它。
我在 RSpec 中使用了修改后的 ruby​​ 类型代码,这些代码来自 Postman 生成函数,我仍然无法成功获取访问令牌。我认为这种差异很奇怪。


20160307 PM03:10 更新:

相关文件已放在 at github.com 。我已经通过 Postman 软件获得了访问令牌,但我仍然不明白为什么我可以通过 RSpec 获得访问令牌,尽管已经尝试了许多其他代码来获得。 RSpec 仍未解决。

【问题讨论】:

  • 即使我将 Base64.strict_encode64 替换为 Base64.urlsafe_encode64 谓词,它仍然是相同的错误描述。

标签: ruby-on-rails rspec access-token doorkeeper invalid-argument


【解决方案1】:

我从未使用过 Doorkeeper gem,我使用的是 Devise Token Auth。我检查了文档并发现了这一点。您可以存根 :doorkeeper_token 方法来测试受保护的方法。

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

【讨论】:

  • 感谢您的回答,但这不是我想要的。我想要的是如何测试获取我的 API 的访问令牌,这些 API 是由 Grape 构建的,而不是在 Rails 的控制器中,而不跳过授权流程的过程。
  • 需要注意的是,您的登录名不应该是post "http://someaddress", params, headers
  • 为什么?如果没有登录,我无法获得 OAuth 客户端的授权代码。顺便说一句,这个post谓词是根据RFC 6750发送获取访问令牌的请求的谓词。
  • 好的,在你修改评论之后,我想我明白你的意思了。我稍后会尝试。
  • 是的,我当时正在使用移动应用程序。顺便说一句,我相信 Doorkeeper 的规范测试已经涵盖了令牌身份验证。相反,您可能只想测试您的控制器的行为是否符合您的预期,以返回某些代码并正确地对数据进行 CRUD。将身份验证存根将帮助您了解操作的核心。 github.com/doorkeeper-gem/doorkeeper/blob/master/spec/…
【解决方案2】:

对不起,我想我在尝试了很多事情后误解了 RSpec 测试用例的 it-statement 用法。

RSpec 中 it 语句的正确含义:每个 it 语句 彼此独立,即使它们处于相同的上下文或描述语句下。

因此,如果我将请求授权代码和请求访问令牌的两个 it 语句组合在一起,我将在通过 RSpec 测试的过程中完成一半。

我需要解决的另一件事是在 HTTP 标头中使用正确的授权编码来获取访问令牌。请从 Base64.urlsafe_encode64 更改为 Base64.strict_encode64

以下是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

【讨论】:

    猜你喜欢
    • 2016-11-06
    • 2021-05-31
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    • 2016-12-03
    • 2019-09-16
    相关资源
    最近更新 更多