Class: OAuth::Consumer — Documentation by YARD 0.9.44

# Class: OAuth::Consumer

Inherits:
  Object
  - Object
  - OAuth::Consumer
  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 {OAuth::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",
           # Token endpoint redirects are followed only within the same origin by
          # default. Cross-origin redirects can re-sign token requests for an
          # attacker-controlled endpoint, so they require explicit opt-in.
          token_request_max_redirects: 10,
          token_request_cross_origin_redirects: false,
        },
      )

## Instance Attribute Summary collapse

- #http ⇒ Object

  The HTTP object for the site.

- #key ⇒ String

  OAuth consumer key.

- #options ⇒ Hash

  Consumer configuration options.

- #secret ⇒ String

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

- #site ⇒ Object

## Instance Method Summary collapse

- #access_token_path ⇒ Object
- #access_token_url ⇒ Object
- #access_token_url? ⇒ Boolean
- #authenticate_path ⇒ Object
- #authenticate_url ⇒ Object
- #authenticate_url? ⇒ Boolean
- #authorize_path ⇒ Object
- #authorize_url ⇒ Object
- #authorize_url? ⇒ Boolean
- #create_signed_request(http_method, path, token = nil, request_options = {}, *arguments) ⇒ Object

  Creates and signs an http request.

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

  Exchanges a verified Request Token for an Access Token.

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

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

- #http_method ⇒ Object

  The default http method.

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

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

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

  Creates, signs and performs an http request.

- #request_endpoint ⇒ Object
- #request_token_path ⇒ Object
- #request_token_url ⇒ Object

  TODO: this is ugly, rewrite.

- #request_token_url? ⇒ Boolean
- #scheme ⇒ Object
- #sign!(request, token = nil, request_options = {}) ⇒ Object

  Sign the Request object.

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

  Return the signature_base_string.

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

  Creates a request and parses the result as url_encoded.

- #token_request_cross_origin?(current_uri, redirected_uri) ⇒ Boolean
- #token_request_cross_origin_redirects?(request_options) ⇒ Boolean
- #token_request_effective_port(uri) ⇒ Object
- #token_request_max_redirects(request_options) ⇒ Object
- #token_request_options(request_options) ⇒ Object
- #token_request_redirect_path(current_uri, redirected_uri) ⇒ Object
- #token_request_redirect_uri(current_uri, response) ⇒ Object
- #token_request_uri(path) ⇒ Object
- #uri(custom_uri = nil) ⇒ Object

  Contains the root URI for this site.

## 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’)

    128
    129
    130
    131
    132
    133
    134
    135
    # File 'lib/oauth/consumer.rb', line 128

    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

### #http ⇒ `Object`

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

    153
    154
    155
    # File 'lib/oauth/consumer.rb', line 153

    def http
      @http ||= create_http
    end

### #key ⇒ `String`

Returns OAuth consumer key.

Returns:

- (`String`) —

  OAuth consumer key

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

### #options ⇒ `Hash`

Returns Consumer configuration options.

Returns:

- (`Hash`) —

  Consumer configuration options

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

### #secret ⇒ `String`

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

Returns:

- (`String`) —

  OAuth consumer secret (redacted in `#inspect`)

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

### #site ⇒ `Object`

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

    def site
      @options[:site].to_s
    end

## Instance Method Details

### #access_token_path ⇒ `Object`

    423
    424
    425
    # File 'lib/oauth/consumer.rb', line 423

    def access_token_path
      @options[:access_token_path]
    end

### #access_token_url ⇒ `Object`

    452
    453
    454
    # File 'lib/oauth/consumer.rb', line 452

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

### #access_token_url? ⇒ `Boolean`

Returns:

- (`Boolean`)

    456
    457
    458
    # File 'lib/oauth/consumer.rb', line 456

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

### #authenticate_path ⇒ `Object`

    415
    416
    417
    # File 'lib/oauth/consumer.rb', line 415

    def authenticate_path
      @options[:authenticate_path]
    end

### #authenticate_url ⇒ `Object`

    436
    437
    438
    # File 'lib/oauth/consumer.rb', line 436

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

### #authenticate_url? ⇒ `Boolean`

Returns:

- (`Boolean`)

    440
    441
    442
    # File 'lib/oauth/consumer.rb', line 440

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

### #authorize_path ⇒ `Object`

    419
    420
    421
    # File 'lib/oauth/consumer.rb', line 419

    def authorize_path
      @options[:authorize_path]
    end

### #authorize_url ⇒ `Object`

    444
    445
    446
    # File 'lib/oauth/consumer.rb', line 444

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

### #authorize_url? ⇒ `Boolean`

Returns:

- (`Boolean`)

    448
    449
    450
    # File 'lib/oauth/consumer.rb', line 448

    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

    296
    297
    298
    299
    300
    # File 'lib/oauth/consumer.rb', line 296

    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_output ⇒ `Object`

    142
    143
    144
    145
    146
    147
    148
    149
    150
    # File 'lib/oauth/consumer.rb', line 142

    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:

- (`OAuth::AccessToken`)

    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    # File 'lib/oauth/consumer.rb', line 182

    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:

- (`OAuth::RequestToken`)

    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    # File 'lib/oauth/consumer.rb', line 224

    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_method ⇒ `Object`

The default http method

    138
    139
    140
    # File 'lib/oauth/consumer.rb', line 138

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

### #proxy ⇒ `Object`

    460
    461
    462
    # File 'lib/oauth/consumer.rb', line 460

    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’ })

    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
    287
    288
    289
    290
    291
    292
    # File 'lib/oauth/consumer.rb', line 260

    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_endpoint ⇒ `Object`

    401
    402
    403
    404
    405
    # File 'lib/oauth/consumer.rb', line 401

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

      @options[:request_endpoint].to_s
    end

### #request_token_path ⇒ `Object`

    411
    412
    413
    # File 'lib/oauth/consumer.rb', line 411

    def request_token_path
      @options[:request_token_path]
    end

### #request_token_url ⇒ `Object`

TODO: this is ugly, rewrite

    428
    429
    430
    # File 'lib/oauth/consumer.rb', line 428

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

### #request_token_url? ⇒ `Boolean`

Returns:

- (`Boolean`)

    432
    433
    434
    # File 'lib/oauth/consumer.rb', line 432

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

### #scheme ⇒ `Object`

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

    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.

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

    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

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

    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, &block) ⇒ `Object`

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

    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
    334
    335
    336
    # File 'lib/oauth/consumer.rb', line 303

    def token_request(http_method, path, token = nil, request_options = {}, *arguments, &block)
      response = request(http_method, path, token, token_request_options(request_options), *arguments)
      case response.code.to_i

      when (200..299)
        if block
          block.call(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)
        current_uri = token_request_uri(path)
        redirected_uri = token_request_redirect_uri(current_uri, response)
        response.error! unless redirected_uri

        redirect_count = request_options[:token_request_redirect_count].to_i + 1
        response.error! if redirect_count > token_request_max_redirects(request_options)
        response.error! if token_request_cross_origin?(current_uri, redirected_uri) &&
          !token_request_cross_origin_redirects?(request_options)

        redirect_options = request_options.merge(token_request_redirect_count: redirect_count)
        token_request(http_method, token_request_redirect_path(current_uri, redirected_uri), token, redirect_options, *arguments, &block)
      when (400..499)
        raise OAuth::Unauthorized, response
      else
        response.error!
      end
    end

### #token_request_cross_origin?(current_uri, redirected_uri) ⇒ `Boolean`

Returns:

- (`Boolean`)

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

    def token_request_cross_origin?(current_uri, redirected_uri)
      current_uri.scheme.to_s.downcase != redirected_uri.scheme.to_s.downcase ||
        current_uri.host.to_s.downcase != redirected_uri.host.to_s.downcase ||
        token_request_effective_port(current_uri) != token_request_effective_port(redirected_uri)
    end

### #token_request_cross_origin_redirects?(request_options) ⇒ `Boolean`

Returns:

- (`Boolean`)

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

    def token_request_cross_origin_redirects?(request_options)
      request_options.fetch(:token_request_cross_origin_redirects, options[:token_request_cross_origin_redirects])
    end

### #token_request_effective_port(uri) ⇒ `Object`

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

    def token_request_effective_port(uri)
      return uri.port if uri.port
      return 443 if uri.scheme == "https"

      80 if uri.scheme == "http"
    end

### #token_request_max_redirects(request_options) ⇒ `Object`

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

    def token_request_max_redirects(request_options)
      request_options[:token_request_max_redirects] || options[:token_request_max_redirects]
    end

### #token_request_options(request_options) ⇒ `Object`

    338
    339
    340
    341
    342
    343
    344
    # File 'lib/oauth/consumer.rb', line 338

    def token_request_options(request_options)
      request_options.merge(token_request: true).tap do |options|
        options.delete(:token_request_redirect_count)
        options.delete(:token_request_max_redirects)
        options.delete(:token_request_cross_origin_redirects)
      end
    end

### #token_request_redirect_path(current_uri, redirected_uri) ⇒ `Object`

    360
    361
    362
    363
    364
    # File 'lib/oauth/consumer.rb', line 360

    def token_request_redirect_path(current_uri, redirected_uri)
      return redirected_uri.to_s if token_request_cross_origin?(current_uri, redirected_uri)

      redirected_uri.request_uri
    end

### #token_request_redirect_uri(current_uri, response) ⇒ `Object`

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

    def token_request_redirect_uri(current_uri, response)
      location = response["location"]
      return if location.nil? || location.to_s.empty?

      current_uri.merge(location)
    end

### #token_request_uri(path) ⇒ `Object`

    346
    347
    348
    349
    350
    351
    # File 'lib/oauth/consumer.rb', line 346

    def token_request_uri(path)
      uri = URI.parse(path)
      return uri if uri.absolute?

      URI.parse(site).merge(path)
    end

### #uri(custom_uri = nil) ⇒ `Object`

Contains the root URI for this site

    158
    159
    160
    161
    162
    163
    164
    165
    # File 'lib/oauth/consumer.rb', line 158

    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