DEV Community

Mustapha
Mustapha

Posted on

Simplifying “Login with Twitter” using Python, Django and Django Rest Framework.

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.

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",
   ),
]
Enter fullscreen mode Exit fullscreen mode

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.

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)
Enter fullscreen mode Exit fullscreen mode

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.

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
                  )
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)