Skip to content

Commit

Permalink
have OAuth correctly handle non-urlencoded request bodies
Browse files Browse the repository at this point in the history
  • Loading branch information
al2o3cr authored and mislav committed Mar 7, 2012
1 parent 865661b commit 32c9178
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 7 deletions.
34 changes: 29 additions & 5 deletions lib/faraday_middleware/request/oauth.rb
Expand Up @@ -11,10 +11,19 @@ module FaradayMiddleware
# The signature is added to the "Authorization" HTTP request header. If the
# value for this header already exists, it is not overriden.
#
# For requests that have parameters in the body, such as POST, this
# middleware expects them to be in Hash form, i.e. not encoded to string.
# This means this middleware has to be positioned on the stack before any
# encoding middleware such as UrlEncoded.
# If no Content-Type header is specified, this middleware assumes that the
# body hash should be included in the signature parameters. Otherwise, it only
# includes them if the Content-Type is application/x-www-form-urlencoded, as
# required by the OAuth 1.0 specification.
#
# If the body is a string and the Content-Type is either unspecified or
# application/x-www-form-urlencoded, this middleware re-parses the body
# using Faraday::Utils.parse_nested_query and includes the result in the
# OAuth signature parameters. Thus, this middleware can be included either
# before *or* after UrlEncoded.
#
# All other types of encoding middleware should appear *before* this
# middleware to ensure that Content-Type is set appropriately.
class OAuth < Faraday::Middleware
dependency 'simple_oauth'

Expand Down Expand Up @@ -50,7 +59,22 @@ def oauth_options(env)
end

def body_params(env)
env[:body] || {}
if include_body_params?(env)
if env[:body].respond_to?(:to_str)
# same test Faraday::Request::UrlEncoded uses to check for String
# this should exactly reverse that middleware's work
Faraday::Utils.parse_nested_query(env[:body])
else
env[:body] || {}
end
else
{}
end
end

def include_body_params?(env)
# see RFC 5489, section 3.4.1.3.1 for details
env[:request_headers]['Content-Type'].nil? || env[:request_headers]['Content-Type'] == 'application/x-www-form-urlencoded'
end

def signature_params(params)
Expand Down
53 changes: 51 additions & 2 deletions spec/oauth_spec.rb
Expand Up @@ -14,11 +14,12 @@ def auth_values(env)
end
end

def perform(oauth_options = {}, headers = {})
def perform(oauth_options = {}, headers = {}, params = {})
env = {
:url => URI('http://example.com/'),
:request_headers => Faraday::Utils::Headers.new.update(headers),
:request => {}
:request => {},
:body => params
}
unless oauth_options.is_a? Hash and oauth_options.empty?
env[:request][:oauth] = oauth_options
Expand Down Expand Up @@ -98,4 +99,52 @@ def make_app
auth.should_not include('oauth_token')
end
end

context "handling body parameters" do
let(:options) { [{ :consumer_key => 'CKEY',
:consumer_secret => 'CSECRET',
:nonce => '547fed103e122eecf84c080843eedfe6',
:timestamp => '1286830180'}] }

it "does not include the body with a Content-Type that is not application/x-www-form-urlencoded" do
value = { 'foo' => 'bar' }
auth_header_with = auth_header(perform({}, { 'Content-Type' => 'application/json' }, JSON.dump(value)))
auth_header_without = auth_header(perform({}, { 'Content-Type' => 'application/json' }, {}))

auth_header_with.should == auth_header_without
end

it "includes the body parameters with Content-Type application/x-www-form-urlencoded" do
value = { 'foo' => 'bar' }
auth_header_with = auth_header(perform({}, { 'Content-Type' => 'application/x-www-form-urlencoded' }, {}))
auth_header_without = auth_header(perform({}, { 'Content-Type' => 'application/x-www-form-urlencoded' }, value))

auth_header_with.should_not == auth_header_without
end

it "includes the body parameters with an unspecified Content-Type" do
value = { 'foo' => 'bar' }
auth_header_with = auth_header(perform({}, {}, value))
auth_header_without = auth_header(perform({}, { 'Content-Type' => 'application/x-www-form-urlencoded' }, value))

auth_header_with.should == auth_header_without
end

it "includes the body parameters with Content-Type application/x-www-form-urlencoded and a string body" do
value = { 'foo' => 'bar' }
auth_header_hash = auth_header(perform({}, { 'Content-Type' => 'application/x-www-form-urlencoded' }, value))
auth_header_string = auth_header(perform({}, { 'Content-Type' => 'application/x-www-form-urlencoded' }, Faraday::Utils.build_query(value)))
auth_header_string.should == auth_header_hash
end

it "includes the body parameters with Content-Type application/x-www-form-urlencoded and a string body with nested params" do
# simulates the behavior of Faraday::MiddleWare::UrlEncoded
value = { 'foo' => ['bar', 'baz', 'wat'] }
auth_header_hash = auth_header(perform({}, { 'Content-Type' => 'application/x-www-form-urlencoded' }, value))
auth_header_string = auth_header(perform({}, { 'Content-Type' => 'application/x-www-form-urlencoded' }, Faraday::Utils.build_nested_query(value)))
auth_header_string.should == auth_header_hash
end

end

end

0 comments on commit 32c9178

Please sign in to comment.