DEV Community

Cover image for How to integrate Twitter Login API into your React app
Quod AI for Quod AI

Posted on • Updated on • Originally published at quod.ai

How to integrate Twitter Login API into your React app

Getting people to sign up in one click with Twitter is an effective and easy way to divert and convert people to utilize the app.

To grow your app’s customer base, we need to make onboarding as ridiculously easy as possible. We need to ensure that there is almost zero resistance to people signing up for your app. Getting people to sign up in one click using their already existing accounts is an effective and easy way to divert and convert people to utilize the app.

This article was originally posted at: https://quod.ai/post/how-to-integrate-twitter-login-api-into-your-react-app

The usual networks where we can get a significant amount of traffic to convert include the usual suspects such as Facebook, Google, and Twitter. Twitter boasts of 330 million monthly active users in 2021, according to a statistic from Oberlo. Such a number is nothing to sneeze at. It is useful to learn how we can make the sign-up process dead simple for Twitter users.

Objectives

In this tutorial, we will show you:

  1. How to integrate Twitter login to our React app;
  2. How to retrieve user name, profile image, URL, and latest tweet;
  3. And how to log out of our React app.

Let’s get started!

Register the app via the Twitter Development Portal. 

Registration is always the first step in creating an integration into your app. App creation and registration with Twitter is reasonably straightforward.

  1. Head to https://developer.twitter.com and go to the Developer Portal.

  1. Go to a project (or create one if you don’t have one yet). Go to + Add App  > Create New App Instead. Name your app appropriately.

  1. Get the API Key and API Secret key. We will use this later. 

Set up our React application.

Integrating Twitter Login into your app is a highly involved exercise that requires a lot more energy than I think it should. The effort (and the resulting headache) needed to integrate Facebook Login, or Google Login pales into comparison against integrating Twitter. 

Let’s leverage on a working code instead of starting from scratch. Download the demo source code from https://github.com/QuodAI/tutorial-react-twitter-api-login.

git clone git@github.com:QuodAI/tutorial-react-twitter-api-login.git

We will see that we have two main folders: the react folder and the express folder. Let us focus our attention on the react folder for now.

The react folder is a reasonably standard React app created via create-react-app. Go to react/src/App.js to see where most of the magic happens.

Before we proceed any further, we must understand Twitter’s 3-legged OAuth. The whole demo app boils down to securely implementing, in React and Express, the 3-legged OAuth that the OAuth 1.0a protocol specified. The protocol allows this app to perform actions on behalf of a user who explicitly permitted it. In this case, the user authorizes the app to access the user’s display name, profile image, URL, and status (aka “last tweet”).

Step 1: POST oauth/request_token

        //OAuth Step 1
        const response = await axios({
          url: `${apiPath}/twitter/oauth/request_token`, 
          method: 'POST'
        });

        const { oauth_token } = response.data;
Enter fullscreen mode Exit fullscreen mode

View App.js in context at Quod AI

Line 21-27: The app initiates the “dance” by requesting a request token from the endpoint. It does not need any input parameters and will promptly give you both a request token (_oauth_token)_ and a _request token secret (_not visible - resides in the backend).  The request token is a prerequisite for all of the succeeding steps. We may use the request token as the requestor’s identifier. 

Observe: Instead of directly accessing the Twitter API, we instead send the request back to the app.  We shall see how and why we need to “proxy” our calls to an Express backend instead of directly calling the API.

Step 2: GET oauth/authenticate

        //Oauth Step 2
        window.location.href = `https://api.twitter.com/oauth/authenticate?oauth_token=${oauth_token}`;
Enter fullscreen mode Exit fullscreen mode

View App.js in context at Quod AI

Line 28-29: Once we have a request token, the app may redirect the user to oauth/authenticate. We have two scenarios:

  1. If the user is not yet logged in to Twitter, the familiar Twitter login box will display. The user will need to enter his username and password.
  2. If the user is already logged in to Twitter, then we do nothing.

In both scenarios, the API will redirect back to our app. The callback URL is the location where the Twitter API will redirect after the user successfully logs in_._ We will set the callback URL later when we return to configuring our Twitter app in the Development Portal.

The redirect will also give two query parameters: it will give back the request token (_oauth_token)_ and a value called oauth_verifier. 

The existence of the oauth_verifier value signifies that we correctly authenticated the user.

Step 3: POST oauth/access_token

         try {
            //Oauth Step 3
            await axios({
              url: `${apiPath}/twitter/oauth/access_token`,  
              method: 'POST',
              data: {oauth_token, oauth_verifier}
            });
         } catch (error) {
          console.error(error); 
         }
Enter fullscreen mode Exit fullscreen mode

View App.js in context at Quod AI

Line 57-66: We now pass oauth_token, oauth_verifier, and a third value that only resides in the backend server called oauth_token_secret to the oauth/access_token endpoint in exchange for access tokens oauth_access_token and oauth_access_token_secret. 

Note that the access tokens are never exposed to React. They stay hidden in the Express backend to make our app secure.

        try {
          //Authenticated Resource Access
          const {data: {name, profile_image_url_https, status, entities}} = await axios({
            url: `${apiPath}/twitter/users/profile_banner`,
            method: 'GET'
          });

          setIsLoggedIn(true);
          setName(name);
          setImageUrl(profile_image_url_https);
          setStatus(status.text);
          setUrl(entities.url.urls[0].expanded_url);
         } catch (error) {
          console.error(error); 
         }
Enter fullscreen mode Exit fullscreen mode

View App.js in context at Quod AI

Once we obtained the access tokens, we can now access all the Twitter API available to us. Our /twitter/users/profile_banner invokes the twitter API https://api.twitter.com/1.1/account/verify_credentials.json to retrieve and display the user’s name, profile image, blog, and latest tweet.

You may visit https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens to read more about the 3-legged OAuth.

Why can’t we integrate Twitter login with React alone?


Although this tutorial is entitled “How to integrate Twitter Login API into your React app,” we cannot do this with React alone (unlike OAuth 2.0-based logins such as Facebook and Google). We need an Express server to serve as our backend. 

The necessity of a server-side application stems from 1.) OAuth 1.0a protocol requirements and 2). two fundamental limitations of React applications.

  • React cannot hide secrets.

OAuth 1.0a requires us to keep an API secret (a.k.a CONSUMER_SECRET) in our code (the one we kept earlier) to generate signatures for every OAuth call. The browser needs to download our code in full for it to run our React app. Regardless of how you minify or uglify the codebase, the API secret will be there for the world to see. We use Express as the server-side component that conceals this secret.

  • React cannot access the Twitter API directly.

Our JavaScript code cannot access the Twitter API because it does not permit us to do so. By default, we can only access API’s with the same origin as the calling code (this restriction is known as Same-Origin Policy). There are mechanisms to bypass this restriction (such as CORS), but Twitter decided not to go this route. Because of this, we need a server-side component to call the API calls for us (a.k.a “proxy”). 

Set up our Express backend.

For the most part, our backend code is a just thin wrapper around the Twitter API’s to overcome the limitations discussed.  We can analyze express/src/index.js and discuss the implementation.

//OAuth Step 1
router.post('/twitter/oauth/request_token', async (req, res) => {

  const {oauth_token, oauth_token_secret} = await oauth.getOAuthRequestToken();

  res.cookie(COOKIE_NAME, oauth_token , {
    maxAge: 15 * 60 * 1000, // 15 minutes
    secure: true,
    httpOnly: true,
    sameSite: true,
  });

  tokens[oauth_token] = { oauth_token_secret };
  res.json({ oauth_token });

});
Enter fullscreen mode Exit fullscreen mode

View index.js in context at Quod AI

//OAuth Step 3
router.post('/twitter/oauth/access_token', async (req, res) => {


  try {
    const {oauth_token: req_oauth_token, oauth_verifier} = req.body;
    const oauth_token = req.cookies[COOKIE_NAME];
    const oauth_token_secret = tokens[oauth_token].oauth_token_secret;

    if (oauth_token !== req_oauth_token) {
      res.status(403).json({message: "Request tokens do not match"});
      return;
    }

    const {oauth_access_token, oauth_access_token_secret} = await oauth.getOAuthAccessToken(oauth_token, oauth_token_secret, oauth_verifier);
    tokens[oauth_token] = { ...tokens[oauth_token], oauth_access_token, oauth_access_token_secret };
    res.json({success: true});

  } catch(error) {
    res.status(403).json({message: "Missing access token"});
  } 

});
Enter fullscreen mode Exit fullscreen mode

View index.js in context at Quod AI

We can easily see that the Oauth steps in React have counterparts in Express.

We see Step 1 at Lines 26 - 41, Step 3 at Lines 44 - 66, and the authenticated API resource access at  Lines 69 - 80. We need not implement Step 2 anymore, as it is a simple browser redirect.  Observe that we have direct access to oauth_token_secret, oauth_access_token, and oauth_access_token_secret here, unlike in React.

  const CONSUMER_KEY = process.env.CONSUMER_KEY;
  const CONSUMER_SECRET = process.env.CONSUMER_SECRET;
  const _oauth = new (require('oauth').OAuth)(
      'https://api.twitter.com/oauth/request_token',
      'https://api.twitter.com/oauth/access_token',
      CONSUMER_KEY, // consumer key
      CONSUMER_SECRET, // consumer secret
      '1.0',
      oauthCallback,
      'HMAC-SHA1'
  );
Enter fullscreen mode Exit fullscreen mode

View oauth-promise.js in context at Quod AI

Line 6-16: If you are looking for the CONSUMER_SECRET, it is neatly and quietly tucked away inside lib/oauth-promise to initialize the oauth library. We will get back to the oauth library later.

  res.cookie(COOKIE_NAME, oauth_token , {
    maxAge: 15 * 60 * 1000, // 15 minutes
    secure: true,
    httpOnly: true,
    sameSite: true,
  });
Enter fullscreen mode Exit fullscreen mode

View index.js in context at Quod AI

The only data visible to React is the request token (oauth_token), which we will also use as our identity token. Lines 31 - 36 shows how we created a cookie out of the oauth_token. We do this for user identification on subsequent visits. We ensure that nothing else is in the cookie aside from the oauth_token.

On lines 33 - 35, we follow Tania Rascia’s recommendation on how to do cookie-based authentication in Single Page Applications, such as the one we have. As such:

  • secure: true - ensures that the browser does not send cookies if not using HTTPS, to prevent man-in-the-middle attacks;
  • sameSite: true - ensures that the browser does not send cookies if the origin of the script and our Express endpoint are not the same, to mitigate Cross-Site Request Forgery (CSRF) attacks;
  • httpOnly: true - ensures that the client code has no access to cookies (i.e., calling document.cookies will not reveal this cookie). This is to protect us in cases of Cross-Site Scripting (XSS) attacks.
//our in-memory secrets database.
//Can be a key-value store or a relational database
let tokens = {};
Enter fullscreen mode Exit fullscreen mode

View index.js in context at Quod AI

Line 12-14: Where do we keep all the other tokens that need to persist during the OAuth dance? On line 14, we implement a naive and straightforward in-memory object that will survive the session jumps for the most part. The in-memory object storage works well in development machines (as long as you do not restart your Express server). It surprisingly works in serverless deployments, although lambda functions (such as those provided by AWS or Netlify) do not guarantee to keep in-memory states at all and thus may drop it anytime.     

As this is a demonstration, we keep it simple to prevent unneeded complications that will distract us from integrating a Twitter login mechanism. We recommend using either a key-value store, a document store, or a database to keep these tokens for production systems.

const oauth = require('./lib/oauth-promise')(oauthCallback);
Enter fullscreen mode Exit fullscreen mode

View index.js in context at Quod AI

At Line 9, we use the oauth library to spare us the agony and terror of writing the OAuth 1.0a protocol from scratch, with its crazy signature generation and cryptic Twitter response messages. Such is the pain that I experienced when I foolishly attempted to implement it by myself. I  do not wish my worst enemies to experience such torment.

 

  const oauth ={
    getOAuthRequestToken: () => { 
      return new Promise((resolve, reject) => {
        _oauth.getOAuthRequestToken((error, oauth_token, oauth_token_secret, results) => {
          if(error) {
            reject(error);  
          } else {
            resolve({oauth_token, oauth_token_secret, results});  
          }
        });
      });
    },

    getOAuthAccessToken: (oauth_token, oauth_token_secret, oauth_verifier) => { 
      return new Promise((resolve, reject) => {
        _oauth.getOAuthAccessToken(oauth_token, oauth_token_secret, oauth_verifier, (error, oauth_access_token, oauth_access_token_secret, results) => {
          if(error) {
            reject(error);  
          } else {
            resolve({oauth_access_token, oauth_access_token_secret, results});  
          }
        });
      });
    },

    getProtectedResource: (url, method, oauth_access_token, oauth_access_token_secret) => {
       return new Promise((resolve, reject) => {
        _oauth.getProtectedResource(url, method, oauth_access_token, oauth_access_token_secret,  (error, data, response) => {
          if(error) {
            reject(error);  
          } else {
            resolve({data, response});  
          }
        });
      });   
    }

  };
Enter fullscreen mode Exit fullscreen mode

View oauth-promise.js in context at Quod AI

Line 18-55: We wrapped the oauth library nicely within lib/oauth-promise to deal with async and await calls instead of messy callbacks that seriously hurt the eyes.

router.post("/twitter/logout", async (req, res) => {

  try {
    const oauth_token = req.cookies[COOKIE_NAME];
    delete tokens[oauth_token];
    res.cookie(COOKIE_NAME, {}, {maxAge: -1});
    res.json({success: true});
  } catch(error) {
    res.status(403).json({message: "Missing, invalid, or expired tokens"});
  } 

});
Enter fullscreen mode Exit fullscreen mode

View index.js in context at Quod AI

Line 82 - 93: Logging out is a matter of expiring our cookie to force the user to request a new oauth_token.

Connect Frontend with Backend.


For maximum security for our application, both React and Express need to reside in one site.  Having them in one domain prevents a whole range of headaches such as CORS enabling and  XHR cookie passing.

We achieve this in development by taking advantage of create-react-app’s proxy feature. A quick peek at react/package.json reveals the following:

  "name": "tutorial-react-twitter-api-login",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:3000",
Enter fullscreen mode Exit fullscreen mode

Line 2-5: The proxy property tells create-react-app to reroute all paths to http://localhost:3000 if React is not able to handle such route. Port 3000 happens to be the default port that Express listens to, so if you are using default ports, there’s not much to do here.

Note that this only works in development. Deploying React and Express in one site for production is outside the scope of this tutorial.

Launch our ngrok tunnels.

We need Twitter to see our application, especially once the redirect happens after Step 2. Hosting our application locally is not enough; we need to expose the app to the outside world.

We run ngrok to listen to port 8080, which is React’s default development port.

ngrok http 8080

We take note of the URL (HTTPS version), as we will use it later.

Run both React and Express locally.

We will need to export several environment variables for our application to run properly:

export FRONTEND_URL=https://49d6c448c952.ngrok.io            

export HOST=localhost                                   

export DANGEROUSLY_DISABLE_HOST_CHECK=true              

export CONSUMER_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXX       

export CONSUMER_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXX    

  • FRONTEND_URL is the ngrok HTTPS URL. We use this to let our oauth library know that this is the callback URL.
  • We keep HOST to localhost. It is a requirement when using the proxy feature.
  • We keep  DANGEROUSLY_DISABLE_HOST_CHECK to true. ngrok and proxy don’t work well together without this flag. See this issue (https://github.com/facebook/create-react-app/issues/2271) for more details. 
  • CONSUMER_KEY and CONSUMER_SECRET are the API KEY and API secret key that we obtained earlier.

Once everything is set, we can now run both react and express on separate terminals. Note that you need to set the environment variables for both terminals.

cd react && npm start

cd express && npm start

Reconfigure Twitter app callback URLs.

As a final step, we need to register our ngrok URL. Proceed back to the Developer Portal. Go to your app > Settings > Authentication settings > Edit.

  • Enable the  3-legged OAuth
  • Add the ngrok URL to the list of allowed callback URLs. Note: the URL must NOT have a trailing slash, or else it won’t work

Time to view our app!

At last, we reach the exciting bit. Open your browser, and go to the ngrok URL (https://49d6c448c952.ngrok.io/). If fully working, you should see the following screens:

You can view a live demo here to compare: https://pensive-snyder-a1edac.netlify.app

You can view our source code for this tutorial at: https://github.com/QuodAI/tutorial-react-twitter-api-login

Happy coding!

Top comments (0)