0

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

这是我当前的工作代码,**第三方*与我使用可以访问其他帐户的帐户访问 API 的事实有关。这个逻辑主要在scope方法中实现(在这个片段的底部)。

以下包含一些额外的逻辑,但get_grantandpost_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}"}^ }

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

我还在GitHub 中打开了一个问题,但我认为这已经涵盖,只是没有记录(或者我找不到)。

4

1 回答 1

1

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,如果有的话我会在这里发布我的更新

于 2013-03-19T23:52:11.900 回答