使用自定义Omniauth策略时出现“认证失败!invalid_credentials: OAuth2 :: Error”的问题

9

目前我正在开发Rails 4项目,现在我需要连接另一个应用程序(不是单点登录,而是用于访问API),比如说example.com。(注意:example.com使用三脚架oauth安全架构)

经过搜索,我发现我必须实现Omniouth策略。

为此,我参考了这个链接。根据“策略贡献指南”的要求,我已经完成了设置和请求阶段,你可以在这里找到我的样本代码。

require 'multi_json'
require 'omniauth/strategies/oauth2'
require 'uri'

module OmniAuth
  module Strategies
    class MyAppStrategy < OmniAuth::Strategies::OAuth2
      option :name, 'my_app_strategy'

   option :client_options, {
    site: site_url,
    authorize_url: authorize_url,
    request_url: request_url,
    token_url: token_url,
    token_method: :post,
    header: { Accept: accept_header }
  }

  option :headers, { Accept: accept_header }
  option :provider_ignores_state, true

  def consumer
    binding.pry
    ::OAuth::Consumer.new(options.client_id, options.client_secret, options.client_options)
  end

  def request_phase # rubocop:disable MethodLength
    binding.pry
    request_token = consumer.get_request_token({:oauth_callback => callback_url}, options.request_params)
    session["oauth"] ||= {}
    session["oauth"][name.to_s] = {"callback_confirmed" => request_token.callback_confirmed?, "request_token" => request_token.token, "request_secret" => request_token.secret}

    if request_token.callback_confirmed?
      redirect request_token.authorize_url(options[:authorize_params])
    else
      redirect request_token.authorize_url(options[:authorize_params].merge(:oauth_callback => callback_url))
    end

  rescue ::Timeout::Error => e
    fail!(:timeout, e)
  rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
    fail!(:service_unavailable, e)
  end

  def callback_phase # rubocop:disable MethodLength
    fail(OmniAuth::NoSessionError, "Session Expired") if session["oauth"].nil?

    request_token = ::OAuth::RequestToken.new(consumer, session["oauth"][name.to_s].delete("request_token"), session["oauth"][name.to_s].delete("request_secret"))

    opts = {}
    if session["oauth"][name.to_s]["callback_confirmed"]
      opts[:oauth_verifier] = request["oauth_verifier"]
    else
      opts[:oauth_callback] = 'http://localhost:3000/auth/callback' #callback_url
    end

    @access_token = request_token.get_access_token(opts)
    super
    rescue ::Timeout::Error => e
      fail!(:timeout, e)
    rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
      fail!(:service_unavailable, e)
    rescue ::OAuth::Unauthorized => e
      fail!(:invalid_credentials, e)
    rescue ::OmniAuth::NoSessionError => e
      fail!(:session_expired, e)
  end

  def custom_build_access_token
    binding.pry
    verifier = request["oauth_verifier"]
    client.auth_code.get_token(verifier, get_token_options(callback_url), deep_symbolize(options.auth_token_params))
  end
  alias_method :build_access_token, :custom_build_access_token

  def raw_info
    binding.pry
    @raw_info ||= access_token.get('users/me').parsed || {}
  end

  private

  def callback_url
    options[:redirect_uri] || (full_host + script_name + callback_path)
  end

  def get_token_options(redirect_uri)
    { :redirect_uri => redirect_uri }.merge(token_params.to_hash(:symbolize_keys => true))
  end
end
end

结束

我能够重定向到example.com,并且在登录后我能够返回我的callback_phase(你会问怎么知道的,答案是我在callback_phase方法中添加了binding.pry以检查流程)。

但是,在执行策略后,我遇到了以下错误:

ERROR -- omniauth: (my_app_strategy) Authentication failure! invalid_credentials: OAuth2::Error.

调试后发现,我在super调用时出现了这个错误(来自callback_phase方法)。

首先我认为可能存在一些凭据问题,但我能够使用以下内容获取访问令牌(该内容在super调用之前执行)

@access_token = request_token.get_access_token(opts)

此外,更多信息是关于build_access_token的错误,这是oauth2方法。
您可以参考此链接以获取更多信息(只需在页面上搜索build_access_token)。 编辑 - 1 经过调试发现从请求方法中出现了此问题(在进行faraday请求时)。以下是代码片段。
response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
    yield(req) if block_given?
  end

这是我的 Faraday 请求

#<struct Faraday::Request method=:post, path="example.com/oauth/access_token", params={}, headers={"User-Agent"=>"Faraday v0.9.2", "Content-Type"=>"application/x-www-form-urlencoded"}, body={"grant_type"=>"authorization_code", "code"=>"aPexxxvUg", "client_id"=>"xxxxxur303GXEch7QK9k", "client_secret"=>"xxxxxxcad97b3d252e2bcdd393a", :redirect_uri=>"http://localhost:3000/auth/my_app_strategy/callback"}, options=#<Faraday::RequestOptions (empty)>>

我收到以下错误信息:

HTTP状态码 400 - OAuth消费者凭证不足。

有人可以帮忙解决这个问题吗?

是否有其他方式来存储访问令牌,以便我可以用于通信目的。谢谢。


3
你在这里暴露了你的消费者密钥和消费者密钥。我已经试图将它们编辑掉,但在编辑发布之前需要得到其他人的确认。请务必立即取消这些凭据并获取新的保持私密性的凭据。 - Scott Swezey
@ScottS。感谢您的回复,我会注意的。顺便说一下,那些是随机数。 - I-am-simple-user
你为什么没有使用 https://github.com/intridea/omniauth-oauth2/blob/master/lib/omniauth/strategies/oauth2.rb 来设置你的 OAuth? - Ravi Sankar Raju
1
@RaviSankarRaju,是的,实际上我首先尝试使用它,但出现了“oauth_token”不存在的错误。因此,在请求阶段手动创建了消费者并使用它。 - I-am-simple-user
1个回答

4

首先,我想澄清一下Oauth2的工作原理:

Oauth2协议说明如下:

  1. 将用户重定向到提供程序的登录端点并添加一些必需的参数(例如:PROVIDER/public/oauth?redirect_uri=MYWEB/oauthDemo& response_type=code&client_id=ABCDE)。有时还会有一个作用域/权限/资源参数,指示您的目的。

    -> 然后用户进行注册并被重定向到您的端点MYWEB/public/oauth,并获得一个代码

  2. 现在,您需要对提供程序的端点进行POST请求以请求访问令牌。例如:

    POST PROVIDER?code=d5Q3HC7EGNH36SE3N& client_id=d4HQNPFIXFD255H& client_secret=1a98b7cb92407cbd8961cd8db778de53& redirect_uri=https://example.com/oauthDemo& grant_type=authorization_code

  3. 现在,您已经拥有了访问令牌,可以使用它来获取信息或使用JWT对其进行解码。


清楚了这一点,并且看到您的调用似乎是正确的:

  #<struct Faraday::Request method=:post, path="PROVIDER/oauth/access_token", params={}, headers={"User-Agent"=>"Faraday v0.9.2", "Content-Type"=>"application/x-www-form-urlencoded"}, body={"grant_type"=>"authorization_code", "code"=>"aPexxxvUg", "client_id"=>"xxxxxur303GXEch7QK9k", "client_secret"=>"xxxxxxcad97b3d252e2bcdd393a", :redirect_uri=>"MYWEB/auth/my_app_strategy/callback"}, options=#<Faraday::RequestOptions (empty)>>

作为响应,显示 "HTTP状态400 - 不充足的OAuth消费者凭据"。我认为可能是因为以下原因:
a. 提供程序上未正确配置您的客户端。通常在提供程序网站上使用基本配置以便他可以识别您。所以可能没有进行正确配置。
b. 在第一步(重定向到提供程序时)缺少资源/权限/范围参数或配置错误。因此,在请求令牌时会出现问题。

1
与供应商团队进行了讨论,他们表示他们支持 OAuth1 而不是 OAuth2,因此在策略文件中进行了更改。现在它可以正常工作并能够获取详细信息(access_token)。感谢您的回复。 - I-am-simple-user

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接