Simplifying “Login with Twitter” using Python, Django and Django Rest Framework.
Published: 2020-11-27 23:44:27

One thing Twitter is being criticized for is bad documentation for their APIs. Some of the APIs are not well documented. Using Twitter’s documentation sometimes makes you spend a lot of time (days or maybe weeks) trying to implement one thing. Recently, I was working on a Django project with a feature that allows users to login with Twitter. So, I wanted a simple way to interface with Twitter's Oauth1 API using the `requests_oauthlib` library. I was already using the `request_oauthlib` library to interface with other third-party APIs hence I chose to stick with it instead of using an alternative. All the solutions I got online were giving me errors. The problem with those online solutions is that either a required parameter (`oauth_callback_url`) is omitted in sending a request to Twitter’s `auth/request_token` endpoint or a GET request is sent instead of a POST request. When I realized that, I decided to try out something on my own. Fortunately, it worked for me. To spare you the hurdle I went through, this short article aims at guiding you through implementing login with twitter in your Django project. 


As I mentioned earlier, I used the `requests_oauthlib` library. It is important to note that setting up a Django project, creating a Twitter app and doing all the necessary Twitter app configurations are beyond the scope of this article. There are many resources online that can help you with those. But I must mention that, you have to set your callback URL and check the “Allow this app to be used to sign in with Twitter?” option in your twitter app dashboard to avoid your requests being invalidated by Twitter. The callback URL is where you want Twitter to direct users to after successful authentication.


There are three steps in implementing login with Twitter: obtaining a request token, redirecting a user and converting a request token into an access token. With the implementation, it may be difficult to point out where a step ends and the other begins. Before we dive into the implementation of the three steps, include these lines in your url.py file. 
```python 
from django.urls import path
from . import views

urlpatterns += [  
  path(
        "auth/twitter/redirect/",
        views.TwitterAuthRedirectEndpoint.as_view(),
        name="twitter-login-redirect",
   ),
   path(
    "callback/twitter/",
     views.TwitterCallbackEndpoint.as_view(),
     name="twitter-login-callback",
   ),
]
```
We are finally here!
**Step 1: requesting temporary request token.** 
The first step in this flow is to obtain a request token by sending a signed message to `POST oauth/request_token`. There are couple things you must take note here. Firstly, the request must be a post request. And secondly, the body of the request must contain `oauth_callback_url`, which must be the URL encoded version of the URL you want your user to be redirected to. When this URL mismatches the one you set in your app dashboard, Twitter will return an error response.  


**Step 2: redirecting the user.** 
The next step is to direct the users to Twitter so that they may authorize your application. The implementation of this is to redirect a user to `oauth/authenticate` endpoint while passing the oauth_token obtained from the previous step. 
In your view.py file, include the following code.
```python
from requests_oauthlib import OAuth1
from urllib.parse import urlencode
from rest_framework.views import APIView
from django.http.response import  HttpResponseRedirect

class TwitterAuthRedirectEndpoint(APIView):
    def get(self, request, *args, **kwargs):
        try:
            oauth = OAuth1(
                      settings.TWITTER_API_KEY, 
                      client_secret=settings.TWITTER_API_SECRET_KEY
            )
		     #Step one: obtaining request token
            request_token_url = "https://api.twitter.com/oauth/request_token"
            data = urlencode({
                      "oauth_callback": settings.TWITTER_AUTH_CALLBACK_URL
            })
            response = requests.post(request_token_url, auth=oauth, data=data)
            response.raise_for_status()
            response_split = response.text.split("&")
            oauth_token = response_split[0].split("=")[1]
            oauth_token_secret = response_split[1].split("=")[1]  
    		
                #Step two: redirecting user to Twitter
            twitter_redirect_url = (
         f"https://api.twitter.com/oauth/authenticate?oauth_token={oauth_token}"
            )
            return HttpResponseRedirect(twitter_redirect_url)
        except ConnectionError:
             html="<html><body>You have no internet connection</body></html>"
            return HttpResponse(html, status=403)
        except:
              html="<html><body>Something went wrong.Try again.</body></html>"
            return HttpResponse(html, status=403)
```
When a user hits `auth/twitter/redirect/` url, `TwitterAuthRedirectEndpoint` view is executed which makes a post request to `oauth/request_token` with the url encoded version of the callback url in the body of the request. When the request is successfully, the response is parsed into `oauth_token`, and `oauth_secret`. After this, the user is redirected to the url `https://api.twitter.com/oauth/authenticate?oauth_token=oauth_token` containing the `oauth_token` parameter. 
**Step 3: Converting temporary request token to access token.** 
Upon a successful authentication, your callback URL would receive a request containing the query parameters `oauth_token` and oauth_verifier. The final step is to make a post request to oauth/access_token endpoint to exchange the temporary `oauth_token` with an access token. The request must contain the oauth_verifier as a query parameter. Upon successful request, the response would contain the oauth_token and `oauth_token__secret` parameters. These tokens can be stored and used to make request in the future. 
```python
class TwitterCallbackEndpoint(APIView):
         def get(self, request, *args, **kwargs):
               try:
                   oauth_token = request.query_params.get("oauth_token")
                   oauth_verifier = request.query_params.get("oauth_verifier")
                   oauth = OAuth1(
                                     settings.TWITTER_API_KEY,
                                     client_secret=settings.TWITTER_API_SECRET_KEY,
                                     resource_owner_key=oauth_token,
                                     verifier=oauth_verifier,
                    )
                   res = requests.post(
                               f"https://api.twitter.com/oauth/access_token", auth=oauth
                    )
                    res_split = res.text.split("&")
                   oauth_token = res_split[0].split("=")[1]
                   oauth_secret = res_split[1].split("=")[1]
                   user_id = res_split[2].split("=")[1] if len(res_split) > 2 else None
                   user_name = res_split[3].split("=")[1] if len(res_split) > 3 else None
                   f#store oauth_token, oauth_secret, user_id, user_name
                   redirect_url="https: //www.where-to-redirect-users-to"
                  return HttpResponseRedirect(redirect_url)
             except ConnectionError:
                   return HttpResponse(
                             "<html><body>You have no internet connection</body></html>", status=403
                   )
             except:
                  return HttpResponse(
                           "<html><body>Something went wrong.Try again.</body></html>", status=403
                  )
```
The two lines in `TwitterCallbackEndpoint` fetch the oauth_token and `oauth_verifier` Twitter sends to your callback URL. Note that since the `OAuth1` class has the verifier field set, there is no point in setting the `oauth_verifier` parameter to the auth/access_token endpoint. 

Thank you for reading.