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

时间:2013-01-02 13:43:18

标签: ruby oauth

我正在连接到使用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方法(请参阅第一个代码段的底部),但我不确定,反正我无法找到修改它的方法。

我也开了an issue in GitHub,但我认为这已经涵盖了,只是没有记录(或者我找不到)。

1 个答案:

答案 0 :(得分: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,I'll post my updates here if any