Understanding the RequestProxy

75 views
Skip to first unread message

Bill Kocik

unread,
Sep 5, 2009, 10:22:27 AM9/5/09
to OAuth Ruby

I'm trying to integrate OAuth and Typhoeus (http://github.com/pauldix/
typhoeus/tree/master). After much perusal of RDocs and source code, I
think I've finally figured out that what I need to do is either create
a new type of OAuth::RequestProxy, or make use of
OAuth::RequestProxy::MockRequest. I'm not sure which yet.

One thing that's impeding my understanding is this: The signature for
the OAuth::RequestProxy.proxy method is "proxy(request, options = {})"
but everywhere it's called - including in documentation examples - it
is simply passed a hash. No request, and no nil to take its place.

I don't understand how that works. What am I missing here?

Thanks...

Seth Fitzsimmons

unread,
Sep 5, 2009, 2:21:06 PM9/5/09
to oauth...@googlegroups.com
> I'm trying to integrate OAuth and Typhoeus (http://github.com/pauldix/
> typhoeus/tree/master). After much perusal of RDocs and source code, I
> think I've finally figured out that what I need to do is either create
> a new type of OAuth::RequestProxy, or make use of
> OAuth::RequestProxy::MockRequest. I'm not sure which yet.

Awesome. Typhoeus uses curb, right? This OAuth lib is quite tightly
tied to Net::HTTP, though I wish that weren't the case. However,
while building out the CLI (lib/oauth/cli.rb, which is in need of some
refactoring), I ended up making / finding ways to generate signatures,
etc. without using Net::HTTP, so you may be able to re-use some of
that.

As a result, you're probably best of using MockRequest (which was
created for the CLI / testing), as it proxies a Hash.

> One thing that's impeding my understanding is this: The signature for
> the OAuth::RequestProxy.proxy method is "proxy(request, options = {})"
> but everywhere it's called - including in documentation examples - it
> is simply passed a hash. No request, and no nil to take its place.
>
> I don't understand how that works. What am I missing here?

Wow, that is confusing. Since it's being passed a Hash, that's being
used as the stand-in for the "request" object and it's (probably)
instantiating a MockRequest RequestProxy. I'm not sure where / how
the options are actually used; it may be specific for individual
request proxies (e.g. Net::HTTP).

seth

Blaine Cook

unread,
Sep 6, 2009, 6:32:36 AM9/6/09
to oauth...@googlegroups.com
2009/9/5 Seth Fitzsimmons <se...@mojodna.net>:

>
> Awesome.  Typhoeus uses curb, right?  This OAuth lib is quite tightly
> tied to Net::HTTP, though I wish that weren't the case.  However,
> while building out the CLI (lib/oauth/cli.rb, which is in need of some
> refactoring), I ended up making / finding ways to generate signatures,
> etc. without using Net::HTTP, so you may be able to re-use some of
> that.

The OAuth::Consumer class is tightly bound, but the RequestProxy stuff
isn't, so if you're just trying to use the OAuth library to generate
signatures, then you'll want to write a new RequestProxy. I'd
recommend looking at the client/net_http.rb class, and at the various
RequestProxies.

>> One thing that's impeding my understanding is this: The signature for
>> the OAuth::RequestProxy.proxy method is "proxy(request, options = {})"
>> but everywhere it's called - including in documentation examples - it
>> is simply passed a hash. No request, and no nil to take its place.
>>
>> I don't understand how that works. What am I missing here?

I can't speak to the examples (links?) but OAuth::RequestProxy should
definitely take a "request" object; what is a request object is
defined as new subclasses of OAuth::RequestProxy::Base are defined, as
in:

module OAuth::RequestProxy::Net
module HTTP
class HTTPRequest < OAuth::RequestProxy::Base
proxies ::Net::HTTPRequest

from oauth/request_proxy/net_http.rb

So what you'll want to do is write an OAuth::RequestProxy::EasyProxy
(I think that's right, based on a quick glance at Typhoeus) that
normalizes the various parameters from the request into the
formats/escaping expected by OAuth::Signature::* (basically you need
method, uri, and parameters [expected by OAuth] for an outgoing
Request object).

Once you have that, you can look at writing an OAuth::Client that
wraps all the OAuth parameters, signing, and request modification into
a neat package. Once that's done, at least the OAuth::Consumer.sign!
method should work, and the OAuth::Consumer.create_http method can
changed to allow different HTTP classes; SSL and the get_*_token
methods will take a bit more refactoring, though, depending on how the
Typhoeus request class expects SSL and/or parameters.

Hope that helps!

b.

Bill Kocik

unread,
Sep 6, 2009, 9:43:43 AM9/6/09
to oauth...@googlegroups.com
On Sun, Sep 6, 2009 at 6:32 AM, Blaine Cook<rom...@gmail.com> wrote:
>
> 2009/9/5 Seth Fitzsimmons <se...@mojodna.net>:
>>
>> Awesome.  Typhoeus uses curb, right?

Actually when creating it Paul wrote his own C bindings, but it does
use libcurl like curb.

> The OAuth::Consumer class is tightly bound, but the RequestProxy stuff
> isn't, so if you're just trying to use the OAuth library to generate
> signatures, then you'll want to write a new RequestProxy. I'd
> recommend looking at the client/net_http.rb class, and at the various
> RequestProxies.

That is mainly what I'm trying to do. I want to use Typhoeus to make
my OAuth-authenticated HTTP requests (I'm okay with using OAuth and
Net::HTTP to perform initial authentication), so I'm looking to come
up with a way to use OAuth to sign Typhoeus requests.

> I can't speak to the examples (links?) >

The example is in the OAuth::RequestProxy::MockRequest doc, found
here: http://oauth.rubyforge.org/rdoc/classes/OAuth/RequestProxy/MockRequest.html

request = OAuth::RequestProxy.proxy
"method" => "iq",
"uri" => [from, to] * "&",
"parameters" => {
"oauth_consumer_key" => oauth_consumer_key,
"oauth_token" => oauth_token,
"oauth_signature_method" => "HMAC-SHA1"
}

The generated RDoc is actually a little bit malformatted; I've cleaned
it up above. But, on further inspection, I see that most calls in the
OAuth code base do call it with some object in the first slot. It's
only MockRequest that shows using a hash - which is exactly the point
of MockRequest, and I don't know what had me so confused about that.

I also see now the magic in MockRequest: "proxies Hash". That coupled
with how RequestProxy.proxy looks at the class of the passed request
object to decide which of the proxies to use is making things clear to
me now (I think the problem is just that this code is much smarter
than I am). :)

> So what you'll want to do is write an OAuth::RequestProxy::EasyProxy

[snip]
> Hope that helps!

It definitely does. I think for my purposes all I really need to do is
figure out how to use MockRequest to sign requests for Typhoeus.
Because Typhoeus supports get/put/post/delete taking a URI and hash of
parameters, I should be able to wrap those up in methods that use
MockRequest to generate and apply the signature.

Thanks very much for the road map, gentlemen. I have a much better
understanding of how this all works now - and it's really quite
clever, I must say.

--
Bill Kocik

http://bkocik.net

Bill Kocik

unread,
Sep 12, 2009, 11:39:56 AM9/12/09
to OAuth Ruby

Alright, I'm stuck (and possibly incompetent). Maybe my question is
simpler than I think it is. Suppose I have the following:

An OAuth::Consumer object (created with key, secret, and :site
options).
An OAuth::AccessToken
Information about a pending HTTP request in the form of a uri string,
and a hash of parameters such as {:foo => 'bar'}

When all's said and done, I can call this protected resource like
this:

get(uri, :parameters => my_params_hash, :headers = {whatever headers I
need to pass})

Is there a way I can use the OAuth gem to sign this request prior to
the get call in order to access a protected resource requiring user
credentials? (Or at least come up with the signature string that I can
pass as my Authorization header?)

Seth Fitzsimmons

unread,
Sep 12, 2009, 2:40:50 PM9/12/09
to oauth...@googlegroups.com
In order to do what you're suggesting, you have to bypass
OAuth::Consumer completely. However, given the same information, you
can still create a signed request. Here's an example that does that:

http://github.com/mojodna/oauth/blob/1ec3488989a98bed22b4682f78bb25abea81edf6/lib/oauth/cli.rb#L106-165

seth

Bill Kocik

unread,
Sep 16, 2009, 10:00:33 AM9/16/09
to OAuth Ruby

Seth -

Thanks for your continued patience and help. I think I'm getting close
(ish). Here's what I have currently:

http://gist.github.com/185979

I'm getting "Incorrect signature" errors from Twitter with that. Do
you see something I'm doing obviously wrong?

On Sep 12, 2:40 pm, Seth Fitzsimmons <s...@mojodna.net> wrote:
> In order to do what you're suggesting, you have to bypass
> OAuth::Consumer completely.  However, given the same information, you
> can still create a signed request.  Here's an example that does that:
>
> http://github.com/mojodna/oauth/blob/1ec3488989a98bed22b4682f78bb25ab...

Seth Fitzsimmons

unread,
Sep 16, 2009, 2:56:31 PM9/16/09
to oauth...@googlegroups.com
> Thanks for your continued patience and help. I think I'm getting close
> (ish). Here's what I have currently:
>
> http://gist.github.com/185979
>
> I'm getting "Incorrect signature" errors from Twitter with that. Do
> you see something I'm doing obviously wrong?

GitHub is down, otherwise I'd attach what's below.

The short of it is that you need to use `"GET"` as your method instead
of `:get` (the MockRequestProxy isn't smart enough to do the
conversion, which I'd argue is fine--explicit is good). You're also
repeating OAuth params that are already in the `Authorization` header
(don't bother merging your params with those from the proxy if you're
planning on using header auth).

I changed variable (in both senses) strings so that I could compare it
to the command-line output. The signature base string is almost
always the place to look for problems.

seth

$ oauth --consumer-key key --consumer-secret consumer_secret --token
token --secret token_secret --nonce
csWqt5OX63Ggsm0HINfSZNn07WzVFi14LnQM1dUSs --timestamp 1253126550
--method GET --uri http://example.com/foo --parameters "bar:baz" debug

require 'oauth'

def get(*args)
uri = args[0]
params = args[1] || {}
req = OAuth::RequestProxy.proxy(
'method' => "GET",
'uri' => uri,
'parameters' => prepare_parameters(params)
)
req.sign!(:consumer_secret => "consumer_secret", :token_secret =>
"token_secret")
#raise req.oauth_header
puts "URI: #{uri}"
puts "Parameters: #{req.parameters.merge(params).inspect}"
puts "SBS: #{req.signature_base_string}"
puts "Authorization header: #{req.oauth_header}"

# res = Typhoon.get(uri, :params => req.parameters.merge(params),
:headers => {"Authorization" => req.oauth_header})
# raise res.inspect
end

def prepare_parameters(params)
extra_params = {}
params.each do |k,v|
k = k.is_a?(Symbol) ? k.to_s : k
extra_params[k] = v
end

{
"oauth_consumer_key" => "key",
"oauth_nonce" => "csWqt5OX63Ggsm0HINfSZNn07WzVFi14LnQM1dUSs",
"oauth_timestamp" => 1253126550,
"oauth_token" => "token",
"oauth_signature_method" => 'HMAC-SHA1',
"oauth_version" => '1.0'
}.reject { |k,v| v.nil? || v == "" }.merge(extra_params)
end

get("http://example.com/foo", {:bar => "baz"})

Bill Kocik

unread,
Sep 16, 2009, 3:24:07 PM9/16/09
to oauth...@googlegroups.com
I can't thank you enough. Fixing the two problems you note below
solved everything - it works beautifully now. I wasn't sure which of
the proxy's parameters I still needed and which I didn't. I guess I
didn't need any of them. :)

On Wed, Sep 16, 2009 at 2:56 PM, Seth Fitzsimmons <se...@mojodna.net> wrote:

> The short of it is that you need to use `"GET"` as your method instead
> of `:get` (the MockRequestProxy isn't smart enough to do the
> conversion, which I'd argue is fine--explicit is good).  You're also
> repeating OAuth params that are already in the `Authorization` header
> (don't bother merging your params with those from the proxy if you're
> planning on using header auth).

Reply all
Reply to author
Forward
0 new messages