Using OAuth 1.0 to Tweet Changelog Updates Automatically for Users

Developing A Python Twitter Integration

Est Reading Time: 7 min

Devon Bleibtrey

Aug 19, 2020

We recently released automatic posting of changelogs to Twitter to help customers engage with their communities and get feedback on product updates. Outside of release notes there are plenty of other reasons to integrate your application with Twitter and with the Twitter team announcing their new developer portal and API and the potential of subscription services, we wanted to share how we did our integration to help others configure an integration of their own.

Package Selection

A handful of OAuth python packages out there can be used to abstract away a lot of the complexity of three-legged OAuth. Unfortunately, not all of them are maintained, and many of the first search results seemed to point to abandoned projects. The OAuth package that appears to have the largest community around it and continues to be maintained is OAuthLib. Instead of directly using the package, the Requests OAuthlib package provides a few niceties that further reduce your effort.

For interfacing with Twitter, it’s easy to use their REST API directly using the Requests package. Still, we’d recommend using Tweepy, which has an active community, is well tested, and provides a friendly pythonic interface to Twitter. Tweepy comes with Requests OAuthlib installed, so if you’d like to use their wrapper around the package to manage OAuth, that can save a few lines of code too.

Using Requests OAuthlib with Tweepy in Django connecting up to Twitter looks something like the following:

from requests_oauthlib import OAuth1Session

from django.conf import settings
from django.db import models

class Organization():
    twitter_temp_oauth_token = models.CharField(
       max_length=1024, default=None, null=True, db_index=True
    )
    twitter_temp_oauth_token_secret = models.CharField(max_length=1024, default=None, null=True)
    twitter_oauth_token = models.CharField(max_length=1024, default=None, null=True)
    twitter_oauth_token_secret = models.CharField(max_length=1024, default=None, null=True)


    def twitter_authorize(self):
       request_token_url = 'https://api.twitter.com/oauth/request_token'
       authorize_url = 'https://api.twitter.com/oauth/authorize'
       app_callback_url = settings.TWITTER_CALLBACK_URL
       oauth = OAuth1Session(
           settings.TWITTER_API_KEY,
           client_secret=settings.TWITTER_API_SECRET_KEY,
           signature_type='auth_header',
           callback_uri=app_callback_url)
       fetch_response = oauth.fetch_request_token(request_token_url)

       self.twitter_temp_oauth_token = fetch_response.get('oauth_token')
       self.twitter_temp_oauth_token_secret = fetch_response.get('oauth_token_secret')
       self.save()

       return oauth.authorization_url(authorize_url)

    def twitter_callback(self, oauth_token, oauth_verifier):
       access_token_url = 'https://api.twitter.com/oauth/access_token'
       oauth = OAuth1Session(
           settings.TWITTER_API_KEY,
           client_secret=settings.TWITTER_API_SECRET_KEY,
           resource_owner_key=oauth_token,
           resource_owner_secret=self.twitter_temp_oauth_token_secret,
           verifier=oauth_verifier,
           signature_type='auth_header')
       oauth_tokens = oauth.fetch_access_token(access_token_url)

       self.twitter_oauth_token = oauth_tokens.get('oauth_token')
       self.twitter_oauth_token_secret = oauth_tokens.get('oauth_token_secret')
       self.twitter_temp_oauth_token = None
       self.twitter_temp_oauth_token_secret = None
       self.save()

       return True

    def twitter_tweet(self, content):
       tweet_url = 'https://api.twitter.com/1.1/statuses/update.json'
       oauth = OAuth1Session(
           settings.TWITTER_API_KEY,
           client_secret=settings.TWITTER_API_SECRET_KEY,
           resource_owner_key=self.twitter_oauth_token,
           resource_owner_secret=self.twitter_oauth_token_secret,
           signature_type='auth_header')
       return oauth.post(tweet_url, data={'status': content})

The example above is a naive approach with potential for optimization but will get your application hooked up and able to read and write to your customer’s twitter account.

Authentication

In the code snippet above, there’s a bit going on, so let’s step through some parts to understand better what we’re doing. The first step we take is asking Twitter to provide us with an authorization URL. To do this, we send Twitter our application credentials, which you can get through the Twitter API portal.

def twitter_authorize(self):
   request_token_url = 'https://api.twitter.com/oauth/request_token'
   authorize_url = 'https://api.twitter.com/oauth/authorize'
   app_callback_url = settings.TWITTER_CALLBACK_URL
   oauth = OAuth1Session(
       settings.TWITTER_API_KEY,
       client_secret=settings.TWITTER_API_SECRET_KEY,
       signature_type='auth_header',
       callback_uri=app_callback_url)
   ...

Twitter verifies which app we’d like to prompt a user to grant access to and sends us back a set of temporary request tokens that we can then use to generate an authorization URL.

    ...
    fetch_response = oauth.fetch_request_token(request_token_url)

    self.twitter_temp_oauth_token = fetch_response.get('oauth_token')
    self.twitter_temp_oauth_token_secret = fetch_response.get('oauth_token_secret')
    self.save()
    ...

We need to save these temporary values somewhere so that we can use them to correlate the non-temporary tokens generated by Twitter upon a user granting authorization to our application. You could save them in memory, a cache, or like we are doing above, in a database.

   return oauth.authorization_url(authorize_url)

With these temporary tokens, we can now generate an authorization URL that we can use to redirect users to Twitter, where they'll be asked for permission to access their account.

In our application, we return the authorization URL to our frontend application, which uses it as a hyperlink that customers can click to authorize access to Twitter.

Once a user clicks the link and grants permission to your application, Twitter will use a callback URL that you've predefined in the Twitter API Portal to navigate the user back to your site along with approved credentials in the query params of the URL. The query params include oauth_token and oauth_verifier which are the two parameters we pass to our twitter_callback method.

def twitter_callback(self, oauth_token, oauth_verifier):
   access_token_url = 'https://api.twitter.com/oauth/access_token'
   oauth = OAuth1Session(
       settings.TWITTER_API_KEY,
       client_secret=settings.TWITTER_API_SECRET_KEY,
       resource_owner_key=oauth_token,
       resource_owner_secret=self.twitter_temp_oauth_token_secret,
       verifier=oauth_verifier,
       signature_type='auth_header')
   oauth_tokens = oauth.fetch_access_token(access_token_url)
   ...

We use these values to fetch the non-temporary access tokens that can be used to interface with Twitter on behalf of our users.

After we receive these values, we can delete the temporary request tokens and store off the access tokens for future use.

   ...
   self.twitter_oauth_token = oauth_tokens.get('oauth_token')
   self.twitter_oauth_token_secret = oauth_tokens.get('oauth_token_secret')
   self.twitter_temp_oauth_token = None
   self.twitter_temp_oauth_token_secret = None
   self.save()

   return True
   ...

Now that we have access tokens, we can start interfacing with the Twitter API and tweeting for our users. We have to provide both our application keys and the individual user's access keys to access the API so that Twitter knows which application is making the request for which user and whether or not that user has granted the necessary permissions to your application.

def twitter_tweet(self, content):
   tweet_url = 'https://api.twitter.com/1.1/statuses/update.json'
   oauth = OAuth1Session(
       settings.TWITTER_API_KEY,
       client_secret=settings.TWITTER_API_SECRET_KEY,
       resource_owner_key=self.twitter_oauth_token,
       resource_owner_secret=self.twitter_oauth_token_secret,
       signature_type='auth_header')
   return oauth.post(tweet_url, data={'status': content})

To learn more about three-legged OAuth and additional security elements such as signature types and hashing algorithms, check out some of Twitter's docs:

Getting Started Walkthroughs

There are a lot of awesome resources to help you get started with your Twitter integration. Most of our implementation was formed through the assistance of the following guides:

The easiest walkthrough to follow was twauth-web, but it uses an outdated python OAuth package. So after using it to get started, we transitioned to requests-oauthlib, which works similarly but with the more maintained OAuthlib package. There’s a solid authentication walkthrough for Twitter within the requests-oauthlib repository, but it isn’t easy to find in the read the docs page. You’ll want to check out the docstring for the OAuth1Session class.

If you run into any trouble or have any questions feel free to hit us up :).

Automatic Release Notes. Zero Effort.

Sign up today with instant, no-hassle setup. No credit card required!

Sign Up Now

© 2020 Next Release, LLC. All rights reserved. Made with ❤   in Michigan.