pythonoauthrauth

Reuse OAuth1 authorization tokens with rauth


I have the following implementation of a twitter client using rauth (OAuth1), based on the twitter-timeline-cli.py script in the rauth examples:

from rauth.service import OAuth1Service

class TwitterClient:

    KNOWN_USERS = {
        'user1' : ("xxx", "yyy", "2342354"), # These should be real tokens
    }

    def __init__(self):
        # Get a real consumer key & secret from https://dev.twitter.com/apps/new
        self.twitter = OAuth1Service(
            name='twitter',
            consumer_key=TWITTER_CONSUMER_KEY,
            consumer_secret=TWITTER_CONSUMER_SECRET,
            request_token_url='https://api.twitter.com/oauth/request_token',
            access_token_url='https://api.twitter.com/oauth/access_token',
            authorize_url='https://api.twitter.com/oauth/authorize',
            base_url='https://api.twitter.com/1/')

    def authorize(self):
        request_token, request_token_secret = self.twitter.get_request_token()
        authorize_url = self.twitter.get_authorize_url(request_token)
        print 'Visit this URL in your browser: ' + authorize_url
        pin = raw_input('Enter PIN from browser: ')
        return request_token, request_token_secret, pin

    def init_session(self, user):
        if user in self.KNOWN_USERS :
            request_token, request_token_secret, pin = self.KNOWN_USERS[user]
        else:
            request_token, request_token_secret, pin = self.authorize()
        session = self.twitter.get_auth_session(request_token,
                                                request_token_secret,
                                                method='POST',
                                                data={'oauth_verifier': pin})
        return session

    def list_tweets(self, user):
        session = self.init_session(user)
        params = {'include_rts': 1,  # Include retweets
                  'count': 10}       # 10 tweets

        r = session.get('statuses/home_timeline.json', params=params)

        for i, tweet in enumerate(r.json(), 1):
            handle = tweet['user']['screen_name'].encode('utf-8')
            text = tweet['text'].encode('utf-8')
            print '{0}. @{1} - {2}'.format(i, handle, text)

tc = TwitterClient()

tc.list_tweets('user1')

The idea is that, if the user is not known, he is requested to authorize the application. If, on the other hand, the user has already authorized this application, the authorization tokens (request_token, request_token_secret, pin) should be reused (normally the tokens would be in a database; for the time being, they are hard-coded in the script)

But this is not working:

Traceback (most recent call last):
  File "my-twitter-timeline-cli.py", line 56, in <module>
    tc.list_tweets('user1')
  File "my-twitter-timeline-cli.py", line 43, in list_tweets
    session = self.init_session(user)
  File "my-twitter-timeline-cli.py", line 39, in init_session
    data={'oauth_verifier': pin})
  File ".../lib/python2.7/site-packages/rauth/service.py", line 326, in get_auth_session
    **kwargs)
  File ".../lib/python2.7/site-packages/rauth/service.py", line 299, in get_access_token
    process_token_request(r, decoder, key_token, key_token_secret)
  File ".../lib/python2.7/site-packages/rauth/service.py", line 25, in process_token_request
    raise KeyError(PROCESS_TOKEN_ERROR.format(key=bad_key, raw=r.content))
KeyError: 'Decoder failed to handle oauth_token with data as returned by provider. A different decoder may be needed. Provider returned: <?xml version="1.0" encoding="UTF-8"?>\n<hash>\n  <error>Invalid / expired Token</error>\n  <request>/oauth/access_token</request>\n</hash>\n'

Is it possible to reuse OAuth1 authorization tokens?


Solution

  • I was misunderstanding the whole process. We do not need to save the request_token, request_token_secret and pin, but the access_token and the access_token_secret.

    The process is actually:

    1. Use request_token, request_token_secret and pin to get access_token and access_token_secret
    2. Save access_token and access_token_secret (to database, or whatever)
    3. Next time, reuse access_token and access_token_secret

    This is my corrected test code:

    from rauth.service import OAuth1Service
    
    class TwitterClient:
    
        KNOWN_USERS = { # (access_token, access_token_secret)
            'user1' : ("xxx", "yyy")
        }
    
        def __init__(self):
            # Get a real consumer key & secret from https://dev.twitter.com/apps/new
            self.twitter = OAuth1Service(
                name='twitter',
                consumer_key=TWITTER_CONSUMER_KEY,
                consumer_secret=TWITTER_CONSUMER_SECRET,
                request_token_url='https://api.twitter.com/oauth/request_token',
                access_token_url='https://api.twitter.com/oauth/access_token',
                authorize_url='https://api.twitter.com/oauth/authorize',
                base_url='https://api.twitter.com/1/')
    
        def new_session(self):
            request_token, request_token_secret = self.twitter.get_request_token()
            authorize_url = self.twitter.get_authorize_url(request_token)
            print 'Visit this URL in your browser: ' + authorize_url
            pin = raw_input('Enter PIN from browser: ')
            session = self.twitter.get_auth_session(request_token,
                                                    request_token_secret,
                                                    method='POST',
                                                    data={'oauth_verifier': pin})
            print session.access_token, session.access_token_secret # Save this to database
            return session
    
        def reuse_session(self, user):
            access_token, access_token_secret = self.KNOWN_USERS[user]
            session = self.twitter.get_session((access_token, access_token_secret))
            return session
    
        def init_session(self, user):
            if user in self.KNOWN_USERS : session = self.reuse_session(user)
            else                        : session = self.new_session()
            return session
    
        def list_tweets(self, user):
            session = self.init_session(user)
            params = {'include_rts': 1,  # Include retweets
                      'count': 10}       # 10 tweets
    
            r = session.get('statuses/home_timeline.json', params=params)
    
            for i, tweet in enumerate(r.json(), 1):
                handle = tweet['user']['screen_name'].encode('utf-8')
                text = tweet['text'].encode('utf-8')
                print '{0}. @{1} - {2}'.format(i, handle, text)
    
    tc = TwitterClient()
    
    tc.list_tweets('user1')