5

We're using Rauth to connect to various OAuth 1 APIs. It works fine for a single request, but trying to do 2 or more requests against the given session results in 401 not authorized errors from the APIs.

Twitter API example:

import requests
from rauth import OAuth1Service
from rauth import OAuth1Session

consumer_key = {the consumer key}
consumer_secret = {the consumer secret}
access_token = {the access token}
access_token_secret = {the access token secret}

oauth_service = OAuth1Service(consumer_key = consumer_key, 
                            consumer_secret = consumer_secret)
oauth_session = oauth_service.get_session(token = (access_token, access_secret))

url = 'https://api.twitter.com/1.1/statuses/home_timeline.json'
params = {'include_rts': 'true'}
r = oauth_session.get(url, params=params) # THIS WORKS
r = oauth_session.get(url, params=params) # THIS RETURNS 401 ERROR

This happens on both Twitter and LinkedIn APIs. How do we execute multiple requests against a single OAuth1Session object?

VERSIONS:
rauth==0.5.4
requests==1.1.0


UPDATE:

Strangely, if the params argument is not included then multiple requests can be made- but once params are included, even if it is an empty dict, we get 401s.

Example 1:

r = oauth_session.get(url) # THIS WORKS
r = oauth_session.get(url) # THIS WORKS

Example 2:

r = oauth_session.get(url, params={}) # THIS WORKS
r = oauth_session.get(url, params={}) # THIS RETURNS 401 ERROR
Yarin
  • 173,523
  • 149
  • 402
  • 512
  • Is something happening between the first and second call to `get`? I just tried with the [Twitter example](https://github.com/litl/rauth/blob/master/examples/twitter-timeline-cli.py) and it seems to work fine. – maxcountryman Apr 26 '13 at 17:20
  • @maxcountryman - No, im calling them exactly as shown per the example. see my update though... – Yarin Apr 26 '13 at 17:21
  • What happens if you get a new session instance? (This is made much easier if you use the service wrapper, btw.) – maxcountryman Apr 26 '13 at 17:29
  • We can create a new session for every request, but that's inefficient and defeats the whole point of a session object, no? As for the service wrapper, that requires generating new tokens each time- but we store and reuse our clients' tokens, so we have no use for the service wrapper. – Yarin Apr 26 '13 at 17:35
  • You can use the service wrapper to spit out new session objects like so: `my_service.get_session(tokens)` which is much cleaner than using the `Session` object each time. You should be able to reuse the session instance however, so it may be a bug. Maybe it would be better to open up a ticket on GitHub? – maxcountryman Apr 26 '13 at 17:44
  • Also please try using: `session.get(..., header_auth=True)` if you aren't already. – maxcountryman Apr 26 '13 at 17:52
  • @maxcountryman- header_auth=True did it! Thanks! (I also updated the example to use the service object, though that didn't effect anything) – Yarin Apr 26 '13 at 18:00
  • @maxcountryman- can you make that last comment an answer so I can give you cred... – Yarin Apr 26 '13 at 18:04

1 Answers1

3

Carrying over from the comments, using session.get(..., header_auth=True) should do the trick. It's hard to say exactly why it doesn't work without this, but for the record, header-based authentication is preferred by the spec and given Twitter's position, I wouldn't be surprised if they also prefer it as a provider.

A quick search reveals dozens upon dozens of reports of their API failing where it ostensibly should work and one remedy is to prefer header authentication. From what I can tell, rauth is signing appropriately, so perhaps this is something to do with the way the provider is showing preference and handling non-header authenticated requests.

Update

It looks like either rauth or Requests was not properly handling params. It's odd because the signature base string and oauth_signature seemed to be correct, in that they were appropriately different on each respective request and the data they operated on seemed to checkout. So it seems like it should have validated the request.

At any rate, to correct this, we need to deepcopy elements of the request parameters that are mutable types, e.g. dictionaries. I've got a patch that should correct this, so you should be able to use this without header_auth. However, header authentication is the preferred method so I would still recommend it.

maxcountryman
  • 1,562
  • 1
  • 24
  • 51