Class: OAuth::Consumer

Inherits:
Object
  • Object
show all
Includes:
Auth::Sanitizer::FilteredAttributes
Defined in:
lib/oauth/consumer.rb

Overview

Consumer credentials and request configuration for OAuth 1.0 / 1.0a flows.

Includes Auth::Sanitizer::FilteredAttributes so inspect output redacts the consumer secret while leaving non-sensitive configuration visible.

Constant Summary collapse

CA_FILE =
nil
@@default_options =
SnakyHash::SymbolKeyed.new(
  {
    # Signature method used by server. Defaults to HMAC-SHA1
    signature_method: "HMAC-SHA1",
     # default paths on site. These are the same as the defaults set up by the generators
    request_token_path: "/oauth/request_token",
    authenticate_path: "/oauth/authenticate",
    authorize_path: "/oauth/authorize",
    access_token_path: "/oauth/access_token",
     proxy: nil,
    # How do we send the oauth values to the server see
    # https://oauth.net/core/1.0/#consumer_req_param for more info
    #
    # Possible values:
    #
    #   :header - via the Authorize header (Default) ( option 1. in spec)
    #   :body - url form encoded in body of POST request ( option 2. in spec)
    #   :query_string - via the query part of the url ( option 3. in spec)
    scheme: :header,
     # Default http method used for OAuth Token Requests (defaults to :post)
    http_method: :post,
     # Add a custom ca_file for consumer
    # :ca_file       => '/etc/certs.pem'
     # Possible values:
    #
    # nil, false - no debug output
    # true - uses $stdout
    # some_value - uses some_value
    debug_output: nil,
     # Defaults to producing a body_hash as part of the signature but
    # can be disabled since it's not officially part of the OAuth 1.0
    # spec. Possible values are true and false
    body_hash_enabled: true,
     oauth_version: "1.0",
  },
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(consumer_key, consumer_secret, options = {}) ⇒ Consumer

Create a new consumer instance by passing it a configuration hash:

@consumer = OAuth::Consumer.new(key, secret, {
  :site               => "http://term.ie",
  :scheme             => :header,
  :http_method        => :post,
  :request_token_path => "/oauth/example/request_token.php",
  :access_token_path  => "/oauth/example/access_token.php",
  :authorize_path     => "/oauth/example/authorize.php",
  :body_hash_enabled  => false
 })

Start the process by requesting a token

@request_token = @consumer.get_request_token
session[:request_token] = @request_token
redirect_to @request_token.authorize_url

When user returns create an access_token

@access_token = @request_token.get_access_token
@photos=@access_token.get('/photos.xml')


122
123
124
125
126
127
128
129
# File 'lib/oauth/consumer.rb', line 122

def initialize(consumer_key, consumer_secret, options = {})
  @key = consumer_key
  @secret = consumer_secret

  # ensure that keys are symbols
  snaky_options = SnakyHash::SymbolKeyed.new(options)
  @options = @@default_options.merge(snaky_options)
end

Instance Attribute Details

#httpObject

The HTTP object for the site. The HTTP Object is what you get when you do Net::HTTP.new



147
148
149
# File 'lib/oauth/consumer.rb', line 147

def http
  @http ||= create_http
end

#keyString

Returns OAuth consumer key.

Returns:

  • (String)

    OAuth consumer key



# File 'lib/oauth/consumer.rb', line 18

#optionsHash

Returns Consumer configuration options.

Returns:

  • (Hash)

    Consumer configuration options



# File 'lib/oauth/consumer.rb', line 18

#secretString

Returns OAuth consumer secret (redacted in ‘#inspect`).

Returns:

  • (String)

    OAuth consumer secret (redacted in ‘#inspect`)



# File 'lib/oauth/consumer.rb', line 18

#siteObject



345
346
347
# File 'lib/oauth/consumer.rb', line 345

def site
  @options[:site].to_s
end

Instance Method Details

#access_token_pathObject



371
372
373
# File 'lib/oauth/consumer.rb', line 371

def access_token_path
  @options[:access_token_path]
end

#access_token_urlObject



400
401
402
# File 'lib/oauth/consumer.rb', line 400

def access_token_url
  @options[:access_token_url] || (site + access_token_path)
end

#access_token_url?Boolean

Returns:

  • (Boolean)


404
405
406
# File 'lib/oauth/consumer.rb', line 404

def access_token_url?
  @options.key?(:access_token_url)
end

#authenticate_pathObject



363
364
365
# File 'lib/oauth/consumer.rb', line 363

def authenticate_path
  @options[:authenticate_path]
end

#authenticate_urlObject



384
385
386
# File 'lib/oauth/consumer.rb', line 384

def authenticate_url
  @options[:authenticate_url] || (site + authenticate_path)
end

#authenticate_url?Boolean

Returns:

  • (Boolean)


388
389
390
# File 'lib/oauth/consumer.rb', line 388

def authenticate_url?
  @options.key?(:authenticate_url)
end

#authorize_pathObject



367
368
369
# File 'lib/oauth/consumer.rb', line 367

def authorize_path
  @options[:authorize_path]
end

#authorize_urlObject



392
393
394
# File 'lib/oauth/consumer.rb', line 392

def authorize_url
  @options[:authorize_url] || (site + authorize_path)
end

#authorize_url?Boolean

Returns:

  • (Boolean)


396
397
398
# File 'lib/oauth/consumer.rb', line 396

def authorize_url?
  @options.key?(:authorize_url)
end

#create_signed_request(http_method, path, token = nil, request_options = {}, *arguments) ⇒ Object

Creates and signs an http request. It’s recommended to use the Token classes to set this up correctly



290
291
292
293
294
# File 'lib/oauth/consumer.rb', line 290

def create_signed_request(http_method, path, token = nil, request_options = {}, *arguments)
  request = create_http_request(http_method, path, *arguments)
  sign!(request, token, request_options)
  request
end

#debug_outputObject



136
137
138
139
140
141
142
143
144
# File 'lib/oauth/consumer.rb', line 136

def debug_output
  @debug_output ||= case @options[:debug_output]
  when nil, false
  when true
    $stdout
  else
    @options[:debug_output]
  end
end

#get_access_token(request_token, request_options = {}, *arguments) {|response_body| ... } ⇒ OAuth::AccessToken

Exchanges a verified Request Token for an Access Token.

OAuth 1.0 vs 1.0a:

  • 1.0a requires including oauth_verifier (as returned by the Provider after user authorization) when performing this exchange in a 3‑legged flow.

  • 1.0 flows did not include oauth_verifier.

Usage (3‑legged):

access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier])

Parameters:

  • request_token (OAuth::RequestToken)

    The authorized request token

  • request_options (Hash) (defaults to: {})

    OAuth or request options (include :oauth_verifier for 1.0a)

  • arguments (Array)

    Optional POST body and headers

Yields:

  • (response_body)

    If a block is given, yields the raw response body.

Returns:



176
177
178
179
180
181
182
183
184
185
186
# File 'lib/oauth/consumer.rb', line 176

def get_access_token(request_token, request_options = {}, *arguments, &block)
  response = token_request(
    http_method,
    (access_token_url? ? access_token_url : access_token_path),
    request_token,
    request_options,
    *arguments,
    &block
  )
  OAuth::AccessToken.from_hash(self, response)
end

#get_request_token(request_options = {}, *arguments) {|response_body| ... } ⇒ OAuth::RequestToken

Makes a request to the service for a new OAuth::RequestToken

Example:

@request_token = @consumer.get_request_token

To include OAuth parameters:

@request_token = @consumer.get_request_token(
  oauth_callback: "http://example.com/cb"
)

To include application-specific parameters:

@request_token = @consumer.get_request_token({}, foo: "bar")

OAuth 1.0 vs 1.0a:

  • In 1.0a, the Consumer SHOULD send oauth_callback when obtaining a request token (or explicitly use OUT_OF_BAND) and the Provider MUST include oauth_callback_confirmed=true in the response.

  • This library defaults oauth_callback to OUT_OF_BAND (“oob”) when not provided, which works for both 1.0 and 1.0a, and mirrors common provider behavior.

  • The oauth_callback_confirmed response is parsed by the token classes; it is not part of the signature base string and thus is not signed.

TODO: In a future major release, oauth_callback may be made mandatory unless

request_options[:exclude_callback] is set, to reflect 1.0a guidance.

Parameters:

  • request_options (Hash) (defaults to: {})

    OAuth options for the request. Notably :oauth_callback can be set to a URL, or OAuth::OUT_OF_BAND (“oob”).

  • arguments (Array)

    Optional POST body and headers

Yields:

  • (response_body)

    If a block is given, yields the raw response body.

Returns:



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/oauth/consumer.rb', line 218

def get_request_token(request_options = {}, *arguments, &block)
  # if oauth_callback wasn't provided, it is assumed that oauth_verifiers
  # will be exchanged out of band
  request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND unless request_options[:exclude_callback]

  response = if block
    token_request(
      http_method,
      (request_token_url? ? request_token_url : request_token_path),
      nil,
      request_options,
      *arguments,
      &block
    )
  else
    token_request(
      http_method,
      (request_token_url? ? request_token_url : request_token_path),
      nil,
      request_options,
      *arguments,
    )
  end
  OAuth::RequestToken.from_hash(self, response)
end

#http_methodObject

The default http method



132
133
134
# File 'lib/oauth/consumer.rb', line 132

def http_method
  @http_method ||= @options[:http_method] || :post
end

#proxyObject



408
409
410
# File 'lib/oauth/consumer.rb', line 408

def proxy
  @options[:proxy]
end

#request(http_method, path, token = nil, request_options = {}, *arguments) ⇒ Object

Creates, signs and performs an http request. It’s recommended to use the OAuth::Token classes to set this up correctly. request_options take precedence over consumer-wide options when signing

a request.

arguments are POST and PUT bodies (a Hash, string-encoded parameters, or

absent), followed by additional HTTP headers.

@consumer.request(:get,  '/people', @token, { :scheme => :query_string })
@consumer.request(:post, '/people', @token, {}, @person.to_xml, { 'Content-Type' => 'application/xml' })


254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/oauth/consumer.rb', line 254

def request(http_method, path, token = nil, request_options = {}, *arguments)
  unless %r{^/} =~ path
    @http = create_http(path)
    uri = URI.parse(path)
    path = "#{uri.path}#{"?#{uri.query}" if uri.query}"
  end

  # override the request with your own, this is useful for file uploads which Net::HTTP does not do
  req = create_signed_request(http_method, path, token, request_options, *arguments)
  return if block_given? && (yield(req) == :done)

  rsp = http.request(req)
  # check for an error reported by the Problem Reporting extension
  # (https://wiki.oauth.net/ProblemReporting)
  # note: a 200 may actually be an error; check for an oauth_problem key to be sure
  if !(headers = rsp.to_hash["www-authenticate"]).nil? &&
      (h = headers.grep(/^OAuth /)).any? &&
      h.first.include?("oauth_problem")

    # puts "Header: #{h.first}"

    # TODO: doesn't handle broken responses from api.login.yahoo.com
    # remove debug code when done
    params = OAuth::Helper.parse_header(h.first)

    # puts "Params: #{params.inspect}"
    # puts "Body: #{rsp.body}"

    raise OAuth::Problem.new(params.delete("oauth_problem"), rsp, params)
  end

  rsp
end

#request_endpointObject



349
350
351
352
353
# File 'lib/oauth/consumer.rb', line 349

def request_endpoint
  return if @options[:request_endpoint].nil?

  @options[:request_endpoint].to_s
end

#request_token_pathObject



359
360
361
# File 'lib/oauth/consumer.rb', line 359

def request_token_path
  @options[:request_token_path]
end

#request_token_urlObject

TODO: this is ugly, rewrite



376
377
378
# File 'lib/oauth/consumer.rb', line 376

def request_token_url
  @options[:request_token_url] || (site + request_token_path)
end

#request_token_url?Boolean

Returns:

  • (Boolean)


380
381
382
# File 'lib/oauth/consumer.rb', line 380

def request_token_url?
  @options.key?(:request_token_url)
end

#schemeObject



355
356
357
# File 'lib/oauth/consumer.rb', line 355

def scheme
  @options[:scheme]
end

#sign!(request, token = nil, request_options = {}) ⇒ Object

Sign the Request object. Use this if you have an externally generated http request object you want to sign.



336
337
338
# File 'lib/oauth/consumer.rb', line 336

def sign!(request, token = nil, request_options = {})
  request.oauth!(http, self, token, options.merge(request_options))
end

#signature_base_string(request, token = nil, request_options = {}) ⇒ Object

Return the signature_base_string



341
342
343
# File 'lib/oauth/consumer.rb', line 341

def signature_base_string(request, token = nil, request_options = {})
  request.signature_base_string(http, self, token, options.merge(request_options))
end

#token_request(http_method, path, token = nil, request_options = {}, *arguments) ⇒ Object

Creates a request and parses the result as url_encoded. This is used internally for the RequestToken and AccessToken requests.



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/oauth/consumer.rb', line 297

def token_request(http_method, path, token = nil, request_options = {}, *arguments)
  request_options[:token_request] ||= true
  response = request(http_method, path, token, request_options, *arguments)
  case response.code.to_i

  when (200..299)
    if block_given?
      yield response.body
    else
      # symbolize keys
      # TODO this could be considered unexpected behavior; symbols or not?
      # TODO this also drops subsequent values from multi-valued keys
      CGI.parse(response.body).each_with_object({}) do |(k, v), h|
        h[k.strip.to_sym] = v.first
        h[k.strip] = v.first
      end
    end
  when (300..399)
    # Parse redirect to follow
    uri = URI.parse(response["location"])
    our_uri = URI.parse(site)

    # Guard against infinite redirects
    response.error! if uri.path == path && our_uri.host == uri.host

    if uri.path == path && our_uri.host != uri.host
      options[:site] = "#{uri.scheme}://#{uri.host}"
      @http = create_http
    end

    token_request(http_method, uri.path, token, request_options, arguments)
  when (400..499)
    raise OAuth::Unauthorized, response
  else
    response.error!
  end
end

#uri(custom_uri = nil) ⇒ Object

Contains the root URI for this site



152
153
154
155
156
157
158
159
# File 'lib/oauth/consumer.rb', line 152

def uri(custom_uri = nil)
  if custom_uri
    @uri = custom_uri
    @http = create_http # yike, oh well. less intrusive this way
  else # if no custom passed, we use existing, which, if unset, is set to site uri
    @uri ||= URI.parse(site)
  end
end