How to integrate Twitter Login API into your React app
Tags: react, twitter, tutorial
Published: 2021-02-14 03:27:02

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](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](https://www.oberlo.com/blog/twitter-statistics#:~:text=Here's%20a%20summary%20of%20the,daily%20active%20users%20on%20Twitter). 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](https://developer.twitter.com) and go to the Developer Portal.

  

![](https://uploads-ssl.webflow.com/5e733294d7b5a568ffa60a00/6022413d9ed7db3248aee474_zPPVCxCP75LSaGNeqwHECijjOSQMRexBQc70bm-dC_uVJpmW5SVBRIAxU5-vYEsX0yznUzDgw_xv9zBS8UZvaqQy63CPhROMqG4yBxc-aWZmo4dCbCoetT60V2841Mg-UHemMX0Q.png)

2.  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.

  

![](https://uploads-ssl.webflow.com/5e733294d7b5a568ffa60a00/6022413c28d64f636ec56a9b_IKYTiBVPb3CpSKWVPjfEHb_KeZKW-yLEtbbzMX8NguzqWVGQf4WWTVuUKIjl4PNIpUDA8-Dhq_-DS83nxgn43Yqgj9s1HrSix9ZtmX3nUOLAKh_ZWWVLrov7xFV91Hc3-oOqMXtQ.png)

  

3.  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](https://github.com/QuodAI/tutorial-react-twitter-api-login).  

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

  

![](https://uploads-ssl.webflow.com/5e733294d7b5a568ffa60a00/6022413c096cfe4388f90372_uo982IGdTZF9ld45rxpoAy3ggXjLK74qtyxaAF6d7eVv_i8c4lF3aGPWHw_bP9wCROxvQ8jCQBwFfcCBWY7O3EQW6AdcYaRCnYew2Q7Gh29dBINmfCPfMqzk-_bnt9of7es0sVrR.png)

  

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;
```

View [App.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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}`;
```

View [App.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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); 
         }
```

View [App.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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); 
         }
```

View [App.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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](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](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 });
  
});
```

View [index.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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"});
  } 
  
});
```

View [index.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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'
  );
```

View [oauth-promise.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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,
  });
```

View [index.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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](https://www.taniarascia.com/full-stack-cookies-localstorage-react-express/), 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 = {};
```

View [index.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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);
```

View [index.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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});  
          }
        });
      });   
    }
    
  };
```

View [oauth-promise.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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"});
  } 
  
});
```

View [index.js](https://beta.quod.ai/github/QuodAI/tutorial-react-twitter-api-login) in context at [Quod AI](https://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",
```

**Line 2-5:** The proxy property tells create-react-app to reroute all paths to [http://localhost:3000](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

![](https://uploads-ssl.webflow.com/5e733294d7b5a568ffa60a00/6022413f072ea37ed5075ce0_nz-Zz391uMAUEq8L1n9uzQL1yxwT7k2lZu7ZAtuKVkBuEyVkgV-TYGtHNpPVq8Jng6FNSshhjoxKG5kUWsJxFaEemT_NPg96jVT-mGif0Syxi_UaLHybc2byBgivJkQ8i3htybCJ.png)

  

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](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_**_._

  
  

![](https://uploads-ssl.webflow.com/5e733294d7b5a568ffa60a00/6022413f1033160fe4f5bec3_jTnAyUjiBC_9uhg7e904fDNBOpoN7CAjpOZ1KIEuOeHlyqZ0oafb21GeKIYSR7JTT5q5qt3apwkg8vFotQ4dqjr83dIwRX3sB5xDaarpP129g1zjKaoqA_CHxj5wLIlrPilF6-sM.png)

  

*   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/](https://49d6c448c952.ngrok.io/)). If fully working, you should see the following screens:  
  

![](https://uploads-ssl.webflow.com/5e733294d7b5a568ffa60a00/6022413f28d64f71e6c56a9c_DoETn_ap5P81VZTqHCyf2wYpk6l9NmKaERVb6QbHNNckdkOLHjDZy-k-ed7yAHZN6l81X9taf1tRVuteu-pBYKuL5XV2bagTaJjtm1os3_s5iEwjobVh4vSmzu6IvKRaPzz3pJuf.png)

  

![](https://uploads-ssl.webflow.com/5e733294d7b5a568ffa60a00/6022413f045ee76315d705a0_-r9nhzrDOPN04aI5N_e6q2OlGPLe9zM3BVXuVY8PggN-aah7Ujbe0QZ7Al5Du4eK64R8gtWKnutDsR1Btn8z_dKtn4mocHoMMIC2AmLFgmi44eNq5zPXVGa9jT4itgu8PuQ3s8gr.png)

  

You can view a live demo here to compare: [https://pensive-snyder-a1edac.netlify.app](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](https://github.com/QuodAI/tutorial-react-twitter-api-login)

Happy coding!

‍