OAuth Support

Somehow.


Hi Kenneth,

First, thanks for this wonderful library, you've seen me saying "this is best thing since beer" in Twitter, but I want to emphasize that your work is amazing! 

I was yesterday night, going to work on a small twitter thing. I'm always looking for excuses to use Python Requests :) and I came across it doesn't have Oauth support and I thought "Damn! I'm not using httplib2" (which is the one supported by python-oauth2). So I decided to add it, well at least try it, thus <a href="http://twitter.com/#!/maraujop/status/101080557802360833">I tweeted this</a>.

I'm not an Oauth expert and this is the first time I dive in Python Requests and python-oauth2 code, I'm aware my results are very hacky and need a lot of work. But before progressing any farther, I would like to know your opinions. 

I've used part of python-oauth2 code adapted, I've put a use example in the commit message:

``` python
consumer = oauth.Consumer('consumer-key', 'consumer-secret')
token = oauth.Token('access-key', 'access-key-secret')

response = requests.get('http://api.twitter.com/1/blocks/blocking/ids.json', auth=(consumer, token))
print response.status_code
if response.status_code == 200:
    print response.content
```

This is the file I've based my code on https://github.com/simplegeo/python-oauth2/blob/master/oauth2/__init__.py

python-oauth2 `SignatureMethod` subclass `SignatureMethod_HMAC_SHA1` needs that the request object passed has three things: 

```
request.method
request.normalized_url
request.get_normalized_parameters()
```

`request.method` was the only one already there. I've added the other two. Options are we create our own `SignatureMethod` subclass based in this one, so were are not forced to do have such an strict layout and do hacky things.

This is just a fast hack, done in a couple hours. I can work on it, detach the methods into functions in an oauth.py file or whatever you think better fits the project architecture, just let me know.

Cheers,
Miguel


Thanks for all the kind words, and (more importantly) thanks for this awesome contribution.

I haven't looked at the actual code yet, but my initial thoughts are: 
- I think adding a new OAuth object, or similar would be best (in place of taking over the tuple that gets converted into basicauth)
- I'm not a fan of adding the new `get_normalized_parameters` method, but I can see why it's neccesary. I've been planning on extracting out some of the internal processing into externally-available functions, so you would have something along the lines of: `requests.utils.param_dict_from_url` or something similar.

Random thoughts:
- I've been considering adding a pre-request and post-request processing layer to requests, mostly for internal use. This way the Authentication providers could simply add the headers they need to the requests that are being made, rather than have to deal w/ urllib2's messy auth stuff.


Thank you,
- I first thought about putting everything in an OAuth class, but I'm not sure it could be an `AuthObject` as it is designed for a username/passworda authentication.
- Me neither. If you add those external functions, probably some of this code could be refactored into something cleaner. 

About your thoughts:
- pre-request should be fired right before `opener(req)` so that the request object is already built and can be tampered without side effects. I think that could work and could be a very clean way of attaching auth providers. 

One more thing, my code is not working with POST requests that have params, I rather reorganize everything before adding more complexity to the code. 

I'm not an expert in all these requests libraries: httplib, urllib2... so when you review the code, be gentle :)


Just wanted to let you know, that I made some fixes in my code that make OAuth through GET work like it should. I'm using my version in a twitter script I've just built and it works like a charm.
https://github.com/maraujop/requests/commit/60c0c31660487e2eaf43a6a287f64aa496e6e717

I'm just missing to add some stuff for POST requests, but let me know first how do you want to lay out all this :)


Hooks just landed into develop. Going to try to push a release tonight.


Well done man!

If you keep this development pace, I will have to add you to my "robots camouflaged as devs" mental list, in which you will be next to jezdez.

Anyway, I reviewed your new shinny hooks API and there is only one thing that calls my attention. If you call hooks in `api.py` then I guess `models.Request.send` is meant to be private, so people won't be able to create Requests objects, instead they are forced to use the api. In that case I would rename `send` to `_send` so that it follows your coding convention.

**Good news!** I've moved all the code I had monkey-patched into your project onto a separate hook, which I'm not sure what to name. For now I've originally named it `oauth_hook`. It's working perfectly, though it needs some refactor and POST support (tiny fixes).
- First, how do you want to proceed with hooks? Will you have them as separate files in a `hooks` directory or do you want me to create a github repository with my hook so I mantain it and you lighten Requests core. Maybe you could have a list of hooks in your docs, as if they were sort of plugins.
- Second, will you support hook parameters besides the ones request pass. If not, I will probably have to move my hook, which is a function, into a class method, so that you can import the class and add/override parameters, see what I mean?
- Third, right now every time I call requests I have to add a hooks dictionary parameter as you know. I'm coding two python APIs with OAuth support and Python Requests at the moment and it feels weird to add this parameter every time. So I'm thinking you might want to add a way to attach hooks to requests, when loaded, so that they work all the time. It looks DRYer.
- When you let me know how do you want to go with hooks, I will upload my work. There are several functions that can probably be shortened if you extend a little bit you Request methods, things like `get_normalized_parameters` :)

Damn! I'm so excited with this :D 


Thanks for the enthusiasm :) :sparkles: :cake: :sparkles:

I'll respond to your points when I have some free time later today.

I'm leaning more towards having a requests-oauth module, w/ links in the docs. But, I need to think about this.

Check out the new session feature for automatically attaching:

``` python
with requests.session(hooks=dict(pre_request=oauth_handler) as s:

    s.get(...) #hooks are passed in
```


Thanks! 

Wow, sessions are nice, thanks for the tip, I will look at them. 

I've thinking about the request-oauth module. What I can say is that it is probably going to take a lot of work to maintain it:
- First <a href="https://github.com/simplegeo/python-oauth2">python-oauth2</a> last commit is from 3rd of May. If you have a look into their issues, pull requests and forks, it totally looks like it's not being maintained anymore. People still use this, because there is not many other choices. With the raising popularity of Python Requests, many coders will move to it when this is supported.
- So the code I'm basing the module right now is far from perfect. I'm gonna spend some time, diving into those forks and issues, to make the module better and more stable, but that takes time as you know.
- OAuth2 standard is about to come out, which means we will have to add another module or adding support for both versions in the same module. 

Let me know what you come up with, cheers


any new thoughts on this? :D


I've finally decided to release at as an independent hook. As I said before it is probably going some time maintaining it, with bugs not related to requests itself. You can find my hook here:
https://github.com/maraujop/requests-oauth-hook

Cheers


Beautiful! 

Sorry for the late response. I've been super busy with the upcoming release :)

Can't wait to play with it. I'll give you my thoughts as soon as I do


I know you are busy, that's why I ended up deciding by myself. It's amazing the pace you have working on this.

I'm looking forward to hearing your thoughts :) 

shall I close the issue? Cheers


Thanks :)

Let's leave it open, to remind me to add it to the documentation :)


request-oauth-hook version 0.2.0 is out. It doesn't depend on python-oauth2 anymore. It's got several bug fixes over python-oauth2 and it integrates better with requests, code has been refactored, cleaned and optimized.

This is the best OAuth alternative out there for Python at the moment IMHO. Have you tried it? Are you adding it to requests docs? 

Cheers,
Miguel


Ok, I've seen you have the hook listed under modules.

I've renamed the project to something simpler: requests-oauth https://github.com/maraujop/requests-oauth


update: @idan is working on an oauth module that is library-agnostic. It'll be fully integrated into requests when it is complete.


Closing.