OAuth2 gem:用于第三方访问seek.com中的其他帐户数据的实现



我正在连接到使用OAuth2进行身份验证的API (seek.com.au)。我在OAuth2 gem上挣扎了一段时间,最后编写了如下所示的普通请求。虽然这是工作,但我仍然想了解我最初的OAuth2实现出了什么问题。

这是我目前的工作代码,**第三方*涉及的事实是,我正在访问API与有权限访问其他帐户的帐户。这个逻辑主要是在scope方法中实现的(在这个代码片段的底部)。

下面包括一些额外的逻辑,但get_grantpost_for_token方法应该包括一切。

module Seek::Base
  CONFIG       = YAML.load_file "#{Rails.root}/config/seek.yml"
  HOST         = 'http://test.api.seek.com.au/v1/'
  REQUEST_URIS = {
    get_grant:        HOST + 'OAuth/auth',
    post_for_token:   HOST + 'OAuth/token',
    get_applications: HOST + 'advertiser/applications'
  }
  def uri_for(request, params = {})
    uri  = REQUEST_URIS[request]
    uri += '?' + params.to_param if params.any?
    URI.parse uri
  end
end
class Seek::OAuth2 # TODO? is instance needed?
  include Seek::Base
  # by account_id
  @@tokens = {}
  def initialize(account_id)
    @account_id = account_id 
  end
  def self.authenticate!(account_id)
    new(account_id).authenticate!
  end
  # eg: when a request responded that the token is expired
  def self.expire_token(account_id)
    @@tokens.delete account_id
  end
  ###########################################################################
  ############################### begin #####################################
  # authentication
  # see: http://developer.seek.com.au/docs/partner-api/api-methods/oauth-2.0
  def authenticate!
    @@tokens[@account_id] ||= begin
      grant = get_grant
      raise Exception.new(@error) if @error
      Rails.logger.info "Retrive token for #{@account_id}"
      post_for_token
    end
  end
private
  # part of t he authentication process
  # as we have one account for many entities, we use third party variation
  # see: http://developer.seek.com.au/docs/partner-api/api-methods/oauth2/auth
  def get_grant
    uri         = uri_for :get_grant, {response_type: :code, client_id: username, scope: scope}
    response    = Net::HTTP.get_response uri
    params      = response['location'].split('?').second
    @error      = params.split('error=').second
    @grant_code = params.split('code=').second
  end
  # part of the authentication process
  # see: http://developer.seek.com.au/docs/partner-api/api-methods/oauth2/token
  def post_for_token
    uri = uri_for :post_for_token
    request = Net::HTTP::Post.new uri.path, {'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'}
    request.set_form grant_type: :authorization_code, code: @grant_code, redirect_uri: ''
    request.basic_auth username, password
    response = Net::HTTP.new(uri.host, uri.port).request request
    JSON(response.body)['access_token']
   end    
  ########################## end ############################################
  ###########################################################################
  def username
    CONFIG['credentials']['username']
  end
  def password
    CONFIG['credentials']['password']
  end
  ############## the scope method
  ############## I think I need to insert this in the OAuth request
  def scope
    "urn:seek:thirdparty:username:#{username},urn:seek:advertiser:identity:#{@account_id}"
  end
end

这里有几行(用来代替authenticate!方法),它们本来是要做同样的事情的,但遗憾的是,OAuth返回invalid_client

client = OAuth2::Client.new(username, password, :site => 'http://test.api.seek.com.au/v1')
client.auth_code.authorize_url redirect_uri: ''
token = client.auth_code.get_token 'authorization_code_value',
          headers: {'Authorization' => %^Basic #{Base64.encode64 "#{username}:#{password}"}^ }

我认为这个问题依赖于由OAuth创建的scope方法(见第一个片段的底部),但我不确定,无论如何我找不到修改它的方法。

我也在GitHub中打开了一个问题,但我认为这是覆盖的,只是它没有记录(或者我找不到它)。

Ruby (Rails)实现

这个实现没有使用任何包装器,我尝试了gem OAuth2,但我无法获得授权代码,我猜想是因为第三方实现需要自定义scope,而我无法使用gem设置。

module Api::Base
  CONFIG       = YAML.load_file "#{Rails.root}/config/api.yml"
  HOST         = 'https://api.com.au/v1/'
  REQUEST_URIS = {
    get_grant:        HOST + 'OAuth/auth',
    post_for_token:   HOST + 'OAuth/token',
    get_applications: HOST + 'advertiser/applications'
  }
  def uri_for(request, params = {})
    uri  = REQUEST_URIS[request]
    uri += '?' + params.to_param if params.any?
    URI.parse uri
  end
end
class Api::OAuth2
  include Api::Base
  # by account_id
  @@tokens = {}
  def initialize(account_id)
    @account_id = account_id
  end
  def self.authenticate!(account_id)
    new(account_id).authenticate!
  end
  # eg: when a request responded that the token is expired
  def self.expire_token(account_id)
    @@tokens.delete account_id
  end
  # authentication
  def authenticate!
    @@tokens[@account_id] ||= begin
      grant = get_grant
      raise StandardError.new(@error) if @error
      puts "Retrive token for #{@account_id}"
      post_for_token
    end
  end
private
  # part of t he authentication process
  # as we have one account for many entities, we use third party variation
  def get_grant
    uri          = uri_for :get_grant, {response_type: :code, client_id: username, scope: scope}
    http         = Net::HTTP.new uri.host, uri.port
    http.use_ssl = uri.port == 443
    puts "SSL not set for uri #{uri}" unless http.use_ssl?
    response     = http.get uri.to_s
    raise Exception.new(response.message) unless response.is_a? Net::HTTPFound
    params      = response['location'].split('?').second
    @error      = params.split('error=').second
    @grant_code = params.split('code=').second
  end
  # part of the authentication process
  def post_for_token
    uri      = uri_for :post_for_token
    request  = Net::HTTP::Post.new uri.path, {'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'}
    request.set_form grant_type: 'authorization_code', code: @grant_code, redirect_uri: ''
    request.basic_auth username, password
    http = Net::HTTP.new uri.host, uri.port
    http.use_ssl = uri.port == 443
    response = http.start {|http| http.request request}
    JSON(response.body)['access_token']
   end
  end
  def username
    CONFIG['credentials']['username']
  end
  def password
    CONFIG['credentials']['password']
  end
  def scope
    "urn:api:thirdparty:username:#{username},urn:api:advertiser:identity:#{@account_id}"
  end
end

我仍然计划使用OAuth 2,我会在这里张贴我的更新,如果有

最新更新