class OAuth::Consumer
Constants
- CA_FILE
- CA_FILES
Attributes
Public Class Methods
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')
# File lib/oauth/consumer.rb 106 def initialize(consumer_key, consumer_secret, options = {}) 107 @key = consumer_key 108 @secret = consumer_secret 109 110 # ensure that keys are symbols 111 snaky_options = SnakyHash::SymbolKeyed.new(options) 112 @options = @@default_options.merge(snaky_options) 113 end
Public Instance Methods
# File lib/oauth/consumer.rb 355 def access_token_path 356 @options[:access_token_path] 357 end
# File lib/oauth/consumer.rb 384 def access_token_url 385 @options[:access_token_url] || (site + access_token_path) 386 end
# File lib/oauth/consumer.rb 388 def access_token_url? 389 @options.key?(:access_token_url) 390 end
# File lib/oauth/consumer.rb 347 def authenticate_path 348 @options[:authenticate_path] 349 end
# File lib/oauth/consumer.rb 368 def authenticate_url 369 @options[:authenticate_url] || (site + authenticate_path) 370 end
# File lib/oauth/consumer.rb 372 def authenticate_url? 373 @options.key?(:authenticate_url) 374 end
Creates and signs an http request. It's recommended to use the Token classes to set this up correctly
# File lib/oauth/consumer.rb 274 def create_signed_request(http_method, path, token = nil, request_options = {}, *arguments) 275 request = create_http_request(http_method, path, *arguments) 276 sign!(request, token, request_options) 277 request 278 end
# File lib/oauth/consumer.rb 120 def debug_output 121 @debug_output ||= case @options[:debug_output] 122 when nil, false 123 when true 124 $stdout 125 else 126 @options[:debug_output] 127 end 128 end
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])
@param request_token [OAuth::RequestToken] The authorized request token @param request_options [Hash] OAuth or request options (include :oauth_verifier for 1.0a) @param arguments [Array] Optional POST body and headers @yield [response_body] If a block is given, yields the raw response body. @return [OAuth::AccessToken]
# File lib/oauth/consumer.rb 160 def get_access_token(request_token, request_options = {}, *arguments, &block) 161 response = token_request( 162 http_method, 163 (access_token_url? ? access_token_url : access_token_path), 164 request_token, 165 request_options, 166 *arguments, 167 &block 168 ) 169 OAuth::AccessToken.from_hash(self, response) 170 end
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
ConsumerSHOULD 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.
@param request_options [Hash] OAuth options for the request. Notably
:oauth_callback can be set to a URL, or OAuth::OUT_OF_BAND ("oob").
@param arguments [Array] Optional POST body and headers @yield [response_body] If a block is given, yields the raw response body. @return [OAuth::RequestToken]
# File lib/oauth/consumer.rb 202 def get_request_token(request_options = {}, *arguments, &block) 203 # if oauth_callback wasn't provided, it is assumed that oauth_verifiers 204 # will be exchanged out of band 205 request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND unless request_options[:exclude_callback] 206 207 response = if block 208 token_request( 209 http_method, 210 (request_token_url? ? request_token_url : request_token_path), 211 nil, 212 request_options, 213 *arguments, 214 &block 215 ) 216 else 217 token_request( 218 http_method, 219 (request_token_url? ? request_token_url : request_token_path), 220 nil, 221 request_options, 222 *arguments, 223 ) 224 end 225 OAuth::RequestToken.from_hash(self, response) 226 end
The HTTP object for the site. The HTTP Object is what you get when you do Net::HTTP.new
# File lib/oauth/consumer.rb 131 def http 132 @http ||= create_http 133 end
The default http method
# File lib/oauth/consumer.rb 116 def http_method 117 @http_method ||= @options[:http_method] || :post 118 end
# File lib/oauth/consumer.rb 392 def proxy 393 @options[:proxy] 394 end
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' })
# File lib/oauth/consumer.rb 238 def request(http_method, path, token = nil, request_options = {}, *arguments) 239 unless %r{^/} =~ path 240 @http = create_http(path) 241 _uri = URI.parse(path) 242 path = "#{_uri.path}#{"?#{_uri.query}" if _uri.query}" 243 end 244 245 # override the request with your own, this is useful for file uploads which Net::HTTP does not do 246 req = create_signed_request(http_method, path, token, request_options, *arguments) 247 return if block_given? && (yield(req) == :done) 248 249 rsp = http.request(req) 250 # check for an error reported by the Problem Reporting extension 251 # (https://wiki.oauth.net/ProblemReporting) 252 # note: a 200 may actually be an error; check for an oauth_problem key to be sure 253 if !(headers = rsp.to_hash["www-authenticate"]).nil? && 254 (h = headers.grep(/^OAuth /)).any? && 255 h.first.include?("oauth_problem") 256 257 # puts "Header: #{h.first}" 258 259 # TODO: doesn't handle broken responses from api.login.yahoo.com 260 # remove debug code when done 261 params = OAuth::Helper.parse_header(h.first) 262 263 # puts "Params: #{params.inspect}" 264 # puts "Body: #{rsp.body}" 265 266 raise OAuth::Problem.new(params.delete("oauth_problem"), rsp, params) 267 end 268 269 rsp 270 end
# File lib/oauth/consumer.rb 333 def request_endpoint 334 return if @options[:request_endpoint].nil? 335 336 @options[:request_endpoint].to_s 337 end
# File lib/oauth/consumer.rb 343 def request_token_path 344 @options[:request_token_path] 345 end
TODO: this is ugly, rewrite
# File lib/oauth/consumer.rb 360 def request_token_url 361 @options[:request_token_url] || (site + request_token_path) 362 end
# File lib/oauth/consumer.rb 364 def request_token_url? 365 @options.key?(:request_token_url) 366 end
# File lib/oauth/consumer.rb 339 def scheme 340 @options[:scheme] 341 end
Sign the Request object. Use this if you have an externally generated http request object you want to sign.
# File lib/oauth/consumer.rb 320 def sign!(request, token = nil, request_options = {}) 321 request.oauth!(http, self, token, options.merge(request_options)) 322 end
Return the signature_base_string
# File lib/oauth/consumer.rb 325 def signature_base_string(request, token = nil, request_options = {}) 326 request.signature_base_string(http, self, token, options.merge(request_options)) 327 end
# File lib/oauth/consumer.rb 329 def site 330 @options[:site].to_s 331 end
Creates a request and parses the result as url_encoded. This is used internally for the RequestToken and AccessToken requests.
# File lib/oauth/consumer.rb 281 def token_request(http_method, path, token = nil, request_options = {}, *arguments) 282 request_options[:token_request] ||= true 283 response = request(http_method, path, token, request_options, *arguments) 284 case response.code.to_i 285 286 when (200..299) 287 if block_given? 288 yield response.body 289 else 290 # symbolize keys 291 # TODO this could be considered unexpected behavior; symbols or not? 292 # TODO this also drops subsequent values from multi-valued keys 293 CGI.parse(response.body).each_with_object({}) do |(k, v), h| 294 h[k.strip.to_sym] = v.first 295 h[k.strip] = v.first 296 end 297 end 298 when (300..399) 299 # Parse redirect to follow 300 uri = URI.parse(response["location"]) 301 our_uri = URI.parse(site) 302 303 # Guard against infinite redirects 304 response.error! if uri.path == path && our_uri.host == uri.host 305 306 if uri.path == path && our_uri.host != uri.host 307 options[:site] = "#{uri.scheme}://#{uri.host}" 308 @http = create_http 309 end 310 311 token_request(http_method, uri.path, token, request_options, arguments) 312 when (400..499) 313 raise OAuth::Unauthorized, response 314 else 315 response.error! 316 end 317 end
Contains the root URI for this site
# File lib/oauth/consumer.rb 136 def uri(custom_uri = nil) 137 if custom_uri 138 @uri = custom_uri 139 @http = create_http # yike, oh well. less intrusive this way 140 else # if no custom passed, we use existing, which, if unset, is set to site uri 141 @uri ||= URI.parse(site) 142 end 143 end
Protected Instance Methods
Instantiates the http object
# File lib/oauth/consumer.rb 399 def create_http(_url = nil) 400 _url = request_endpoint unless request_endpoint.nil? 401 402 our_uri = if _url.nil? || _url[0] =~ %r{^/} 403 URI.parse(site) 404 else 405 your_uri = URI.parse(_url) 406 if your_uri.host.nil? 407 # If the _url is a path, missing the leading slash, then it won't have a host, 408 # and our_uri *must* have a host, so we parse site instead. 409 URI.parse(site) 410 else 411 your_uri 412 end 413 end 414 415 if proxy.nil? 416 http_object = Net::HTTP.new(our_uri.host, our_uri.port) 417 else 418 proxy_uri = proxy.is_a?(URI) ? proxy : URI.parse(proxy) 419 http_object = Net::HTTP.new( 420 our_uri.host, 421 our_uri.port, 422 proxy_uri.host, 423 proxy_uri.port, 424 proxy_uri.user, 425 proxy_uri.password, 426 ) 427 end 428 429 http_object.use_ssl = (our_uri.scheme == "https") 430 431 if @options[:no_verify] 432 http_object.verify_mode = OpenSSL::SSL::VERIFY_NONE 433 else 434 ca_file = @options[:ca_file] || CA_FILE 435 http_object.ca_file = ca_file if ca_file 436 http_object.verify_mode = OpenSSL::SSL::VERIFY_PEER 437 http_object.verify_depth = 5 438 end 439 440 http_object.read_timeout = http_object.open_timeout = @options[:timeout] || 60 441 http_object.open_timeout = @options[:open_timeout] if @options[:open_timeout] 442 http_object.ssl_version = @options[:ssl_version] if @options[:ssl_version] 443 http_object.cert = @options[:ssl_client_cert] if @options[:ssl_client_cert] 444 http_object.key = @options[:ssl_client_key] if @options[:ssl_client_key] 445 http_object.set_debug_output(debug_output) if debug_output 446 447 http_object 448 end
create the http request object for a given http_method and path
# File lib/oauth/consumer.rb 451 def create_http_request(http_method, path, *arguments) 452 http_method = http_method.to_sym 453 454 data = arguments.shift if %i[post put patch].include?(http_method) 455 456 # if the base site contains a path, add it now 457 # only add if the site host matches the current http object's host 458 # (in case we've specified a full url for token requests) 459 uri = URI.parse(site) 460 path = uri.path + path if uri.path && uri.path != "/" && uri.host == http.address 461 462 headers = arguments.first.is_a?(Hash) ? arguments.shift : {} 463 464 case http_method 465 when :post 466 request = Net::HTTP::Post.new(path, headers) 467 request["Content-Length"] = "0" # Default to 0 468 when :put 469 request = Net::HTTP::Put.new(path, headers) 470 request["Content-Length"] = "0" # Default to 0 471 when :patch 472 request = Net::HTTP::Patch.new(path, headers) 473 request["Content-Length"] = "0" # Default to 0 474 when :get 475 request = Net::HTTP::Get.new(path, headers) 476 when :delete 477 request = Net::HTTP::Delete.new(path, headers) 478 when :head 479 request = Net::HTTP::Head.new(path, headers) 480 else 481 raise ArgumentError, "Don't know how to handle http_method: :#{http_method}" 482 end 483 484 if data.is_a?(Hash) 485 request.body = OAuth::Helper.normalize(data) 486 request.content_type = "application/x-www-form-urlencoded" 487 elsif data 488 if data.respond_to?(:read) 489 request.body_stream = data 490 if data.respond_to?(:length) 491 request["Content-Length"] = data.length.to_s 492 elsif data.respond_to?(:stat) && data.stat.respond_to?(:size) 493 request["Content-Length"] = data.stat.size.to_s 494 else 495 raise ArgumentError, "Don't know how to send a body_stream that doesn't respond to .length or .stat.size" 496 end 497 else 498 request.body = data.to_s 499 request["Content-Length"] = request.body.length.to_s 500 end 501 end 502 503 request 504 end
# File lib/oauth/consumer.rb 506 def marshal_dump(*_args) 507 {key: @key, secret: @secret, options: @options} 508 end
# File lib/oauth/consumer.rb 510 def marshal_load(data) 511 initialize(data[:key], data[:secret], data[:options]) 512 end