<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Reinforz]]></title><description><![CDATA[Reinforz is an innovative platform for learners, students, and educators to collaborate, reinforce and share their learning with the help of artificial intelligence.]]></description><link>https://blog.reinforz.ai</link><generator>RSS for Node</generator><lastBuildDate>Mon, 11 May 2026 14:21:20 GMT</lastBuildDate><atom:link href="https://blog.reinforz.ai/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to automatically receive messages in discord when a platform is down]]></title><description><![CDATA[Hello everyone! In this blog, we will look at how to automate the process of getting notified in discord if a platform's status is down. It is very helpful as it notifies developers in discord when their website is down. We will learn about the follo...]]></description><link>https://blog.reinforz.ai/automate-sending-platform-status-message-using-discord-webhook</link><guid isPermaLink="true">https://blog.reinforz.ai/automate-sending-platform-status-message-using-discord-webhook</guid><category><![CDATA[Devops]]></category><category><![CDATA[aws ec2]]></category><category><![CDATA[discord]]></category><category><![CDATA[webhooks]]></category><category><![CDATA[cronjob]]></category><dc:creator><![CDATA[Reinforz AI]]></dc:creator><pubDate>Mon, 05 Dec 2022 07:40:53 GMT</pubDate><content:encoded><![CDATA[<p>Hello everyone! In this blog, we will look at how to automate the process of getting notified in discord if a platform's status is down. It is very helpful as it notifies developers in discord when their website is down. We will learn about the following topics in this blog:</p>
<ul>
<li>Using DevOps utilities like webhooks, cron jobs, SSH</li>
<li>Automating deployment to a remote host like an Amazon EC2 Instance</li>
</ul>
<h2 id="heading-requirements">Requirements</h2>
<p>Anyone with a basic knowledge of any coding languages can follow along with this blog. Having a basic knowledge of Git is recommended as well.</p>
<h2 id="heading-make-the-script">Make the Script</h2>
<p>Firstly, we need to make the script we want to run. The script will be a simple Node.js platform status checker running regularly in the background and report to Discord if any of the platforms are down. You can make any script you want. Of course, absolute beginners are urged to follow along with the blog.</p>
<h3 id="heading-webhook">Webhook</h3>
<p>We will use a webhook to message us in discord if any of our servers are down. A webhook is an HTTP-based callback function that allows lightweight, event-driven communication between 2 application programming interfaces (APIs). You can learn more about it <a target="_blank" href="https://www.redhat.com/en/topics/automation/what-is-a-webhook">here</a>.</p>
<p>To create a webhook for a discord server go to a Discord server's settings and follow the path of "<code>Apps</code> &gt; <code>Integrations</code> &gt; <code>Webhooks</code> &gt; <code>New Webhook</code>". The webhook should be created now. Click on it and then click on "<code>Copy Url</code>" as we need it to send messages to discord.</p>
<p>We also have to make HTTP requests in our script. There are a lot of packages available in NPM but I will go with <code>node-fetch</code> in this tutorial. The <code>node-fetch</code> package only needs to be installed if your node version is less than <code>18</code> as it is build into <code>node</code> otherwise. We will install it by using the following command:</p>
<pre><code class="lang-bash">npm install node-fetch
</code></pre>
<h3 id="heading-make-the-script-1">Make the script:</h3>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> fetch <span class="hljs-keyword">from</span> <span class="hljs-string">"node-fetch"</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postplatformstatus</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> allPlatforms = [
    <span class="hljs-comment">// use any urls and check their status </span>
    <span class="hljs-string">'https://api.reinforz.ai/ping'</span>,
    <span class="hljs-string">'https://app.reinforz.ai'</span>,
    <span class="hljs-string">'https://reinforz.ai'</span>,
  ];
  <span class="hljs-keyword">const</span> messages = []; <span class="hljs-comment">// array containing the messages to send</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> platform <span class="hljs-keyword">of</span> allPlatforms) {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(platform);
    <span class="hljs-comment">// We will only notify people if an error has occurred</span>
    <span class="hljs-keyword">if</span> (response.status &gt;= <span class="hljs-number">400</span>) {
      <span class="hljs-comment">// If the status code sent by the response is 400 or higher,</span>
      <span class="hljs-comment">// then it means an error is happening and we will notify relevent people with the platform </span>
      <span class="hljs-keyword">const</span> singleMessage = <span class="hljs-string">`<span class="hljs-subst">${platform}</span> is down. Status: <span class="hljs-subst">${response.status}</span> 🔴`</span>
      <span class="hljs-comment">// If any error response is recieved add it to messages</span>
      <span class="hljs-built_in">console</span>.log(singleMessage);
      messages.push(singleMessage);
    }
  }
  <span class="hljs-keyword">const</span> ROLE_ID = <span class="hljs-string">'&lt;copy a role Id from discord&gt;'</span>;
  <span class="hljs-keyword">if</span> (messages.length !== <span class="hljs-number">0</span>) {
    <span class="hljs-comment">// use the send a message to discord using the webhook URL to see if which servers are inactive.</span>
    <span class="hljs-keyword">await</span> fetch(
      <span class="hljs-string">'&lt;the copied webhook url&gt;'</span>,
      {
        <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
          <span class="hljs-comment">// &lt;@ROLE_ID&gt; is used to notify members with a specific discord role</span>
          <span class="hljs-attr">content</span>: <span class="hljs-string">`&lt;@&amp;<span class="hljs-subst">${ROLE_ID}</span>&gt;\n<span class="hljs-subst">${messages.join(<span class="hljs-string">'\n'</span>)}</span>`</span>,
        }),
      }
    );
  }
}

postplatformstatus();
</code></pre>
<blockquote>
<p><strong>Note</strong>: Make an empty GitHub repository and push the new repository changes.</p>
</blockquote>
<h2 id="heading-make-the-aws-ec2-instance">Make the AWS EC2 instance</h2>
<p>We need a remote host to deploy the script. We are going to use an Amazon EC2 instance for this. It's important to note that while EC2 is a popular choice for hosting virtual machines in the cloud, you can use other hosting providers if you prefer. Creating an EC2 instance involves several steps, which are explained in the <a target="_blank" href="https://docs.aws.amazon.com/efs/latest/ug/gs-step-one-create-ec2-resources.html">official AWS documentation</a>. In general, you will need to select an instance type and size that meets your computing needs, configure your security and networking settings, and then launch the instance. Once your instance is running, you can connect to it using SSH or other remote access tools.</p>
<h2 id="heading-setup-the-aws-ec2-instance">Setup the AWS EC2 instance</h2>
<h3 id="heading-logging-in">Logging in</h3>
<p>After the EC2 instance is up and running, we need to be able to log in to it on our local machine. We will use <a target="_blank" href="https://en.wikipedia.org/wiki/Secure_Shell">SSH</a> to establish a cryptographic network protocol to securely connect to our remote host. To connect to the EC2 instance using SSH, you will need to do the following:</p>
<ol>
<li><p>Download and install an SSH client on your local computer. If you are using a Mac or Linux system or the latest Windows versions, you can use the built-in terminal and SSH command. Older Windows users can use a program like PuTTY.</p>
</li>
<li><p>Locate your EC2 instance's public DNS or IP address. You can find this information in the EC2 Management Console, or by using the ec2-describe-instances command if you are using the AWS CLI.</p>
</li>
<li><p>Use the SSH client to connect to your EC2 instance. The exact command will vary depending on your client and the operating system of your EC2 instance. For example, on a Linux or Mac system you can use the following command:</p>
<pre><code class="lang-bash">ssh -i /path/to/your/private/key.pem ec2-user@your-instance-public-ip
</code></pre>
</li>
<li><p>If this is your first time connecting to the instance, you may be prompted to add the host key to your local SSH known_hosts file. Type <code>yes</code> and press enter to continue.</p>
</li>
</ol>
<p>You should now be logged in to your EC2 instance and able to run commands on the remote system. It's important to note that the exact steps and commands may vary depending on the specific setup of your EC2 instance and your local computer. If you are having trouble connecting, you can check the <a target="_blank" href="https://docs.aws.amazon.com/efs/latest/ug/gs-step-one-create-ec2-resources.html">official AWS documentation</a> or seek help from a qualified AWS professional.</p>
<p>Also, to use the command without linking the <code>/path/to/your/private/key.pem</code> every time like:</p>
<pre><code class="lang-bash">ssh ec2-user@your-instance-public-ip
</code></pre>
<p>Add or Edit the following into the config file in your <code>.ssh</code> folder</p>
<blockquote>
<p><strong>Note:</strong> The path of the file is <code>C:\Users\&lt;Username&gt;\.ssh\config</code> in Windows.</p>
<pre><code>Host &lt;your-instance-public-ip&gt;
    HostName &lt;your-instance-public-ip&gt;
    User ec2-user
    IdentityFile <span class="hljs-string">"/path/to/your/private/key.pem"</span>
</code></pre></blockquote>
<p>To get the private key .pem file that is used in the <code>config</code> file, the SSH connection process, you will need to do the following:</p>
<ol>
<li><p>In the EC2 Management Console, go to the <code>Key Pairs</code> section under the <code>Network &amp; Security</code> menu.</p>
</li>
<li><p>Click on the <code>Create Key Pair</code> button and give your key pair a name. This will download the private key <code>.pem</code> file to your local computer.</p>
</li>
<li><p>Save the <code>.pem</code> file in a safe and secure location on your local computer. You will need this file to connect to your EC2 instance using SSH.</p>
</li>
</ol>
<p>It's important to note that the private key <code>.pem</code> file is essential for accessing your EC2 instance. You should treat it like a password and keep it safe and secure. If you lose the <code>.pem</code> file, you will not be able to connect to your EC2 instance. You should also never share your <code>.pem</code> file with anyone else, as it gives them access to your EC2 instance.</p>
<h3 id="heading-set-up-node">Set up node</h3>
<p>Set up the softwares needed to run your script. I am using node so I will install it. First, I will install Node Version Manager (NVM). It is a command-line utility that allows you to install and manage multiple versions of the Node.js JavaScript runtime on a single computer. We are using NVM to allow our remote host to be able to run other Node.js programs if they require different node versions.</p>
<pre><code class="lang-bash">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash
</code></pre>
<blockquote>
<p><strong>Note:</strong> This will install version 0.39.2, to get the updated one go to this link: https://github.com/nvm-sh/nvm#installing-and-updating.</p>
</blockquote>
<p>Next, we will install the <code>16.x</code> version of Node with the command <code>nvm install 16</code>. Afterwards, verify if node and npm are installed using the commands <code>node -v</code> and <code>npm -v</code> in the terminal of the EC2 instance.
If node and npm are not being recognized do the following:</p>
<ul>
<li>for node:<pre><code class="lang-bash">sudo ln -s /home/&lt;my_user&gt;/.nvm/versions/node/&lt;my_node_version&gt;/bin/node /usr/bin/node
</code></pre>
</li>
<li>for npm:<pre><code class="lang-bash">sudo ln -s /home/&lt;my_user&gt;/.nvm/versions/node/&lt;my_node_version&gt;/bin/npm /usr/bin/npm
</code></pre>
The above commands link the actual node and npm scripts to <code>/usr/bin/</code> because bash recognizes scripts from there.</li>
</ul>
<h3 id="heading-cron-job-in-ec2-instance">Cron job in EC2 instance</h3>
<p>We will now set up a cron job to run on our server. Cron jobs are a standard method of scheduling tasks to run on a machine. Cron is a service running in the background that will execute commands (jobs) at a specified time, or at a regular interval. More details about it can be found <a target="_blank" href="https://www.hostdime.com/kb/hd/command-line/working-with-cron-jobs">here</a>.</p>
<p>Type the following in the terminal to setup a cronjob:</p>
<pre><code class="lang-bash">crontab -e
</code></pre>
<p>Add the following in a new line and save and exit the file using <code>:wq</code>:</p>
<pre><code>*<span class="hljs-regexp">/7 * * * * bash /</span>home/myCronScript.sh
</code></pre><p>This runs the script <code>/home/myCronScript.sh</code> every 7 minutes. Of course, we don't have the bash script now. So let's set them up next.</p>
<h3 id="heading-adding-scripts">Adding scripts</h3>
<p>We need two bash scripts in our EC2 instance: One for the cron job discussed above and another for the Deployment Script. Deployment to a production server typically involves a number of steps, such as building and packaging the application, transferring the package to the production server, and configuring the server to run the application. We need the deployment script to automate installing dependencies on our production server so that it runs without issues.</p>
<ul>
<li><p>The cron script in <code>/home/myCronScript.sh</code>:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
RED=<span class="hljs-string">'\033[0;31m'</span>
NC=<span class="hljs-string">'\033[0m'</span>

<span class="hljs-built_in">cd</span> /home/platform-status

<span class="hljs-keyword">if</span> ! (node /home/platform-status/index.js) <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">printf</span> <span class="hljs-string">"<span class="hljs-variable">${RED}</span>Failed to run platform status checker<span class="hljs-variable">${NC}</span>\n"</span>
  <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>
</code></pre>
</li>
<li><p>The deployment script in <code>/home/myDeployScript.sh</code>:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
RED=<span class="hljs-string">'\033[0;31m'</span>
NC=<span class="hljs-string">'\033[0m'</span>

<span class="hljs-built_in">cd</span> /home/platform-status

<span class="hljs-keyword">if</span> ! (npm install --production) <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">printf</span> <span class="hljs-string">"<span class="hljs-variable">${RED}</span>Failed to install dependencies<span class="hljs-variable">${NC}</span>\n"</span>
  <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>
</code></pre>
<h2 id="heading-deploy-to-ec2-instance">Deploy to EC2 instance</h2>
<p>Finally, we have to find a way to upload to the remote server and run the deploy script automatically. One way to do them is to use GitHub Actions.
GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows us to automate our build, test, and deployment pipeline. Check out <a target="_blank" href="https://docs.github.com/en/actions">their documentation</a> if you want to know more about it.</p>
</li>
</ul>
<p>We are going to add a simple GitHub action to automate deploying to our EC2 instance. Add new folders <code>.github\workflows\</code> in the project directory and put a <code>.yml</code> file on the path, <code>.github\workflows\deploy.yml</code>, and add the following to it:</p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">Script</span>

<span class="hljs-comment"># run the deployment process if any changes come to a js file</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'master'</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'**.js'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'**.json'</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">branch</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v1</span>

    <span class="hljs-comment"># upload to the ec2 host</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">to</span> <span class="hljs-string">Server</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">easingthemes/ssh-deploy@main</span>
      <span class="hljs-attr">env:</span>
        <span class="hljs-attr">SSH_PRIVATE_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SSH_PRIVATE_KEY</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">ARGS:</span> <span class="hljs-string">"-rltgoDzvO --delete"</span>
        <span class="hljs-attr">SOURCE:</span> <span class="hljs-string">""</span>
        <span class="hljs-attr">REMOTE_HOST:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.REMOTE_HOST</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">REMOTE_USER:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SSH_USERNAME</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">TARGET:</span> <span class="hljs-string">"/home/platform-status/"</span>
        <span class="hljs-attr">EXCLUDE:</span> <span class="hljs-string">"/node_modules/, /.github/, /.gitignore, /README.md"</span>

    <span class="hljs-comment"># deploy to the ec2 host</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">production</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">appleboy/ssh-action@v0.1.2</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">host:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.REMOTE_HOST</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SSH_PRIVATE_KEY</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">username:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SSH_USERNAME</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">script:</span> <span class="hljs-string">|</span>
          <span class="hljs-string">bash</span> <span class="hljs-string">/home/myDeployScript.sh</span>
</code></pre>
<p>As you may have noticed, we need to add the secrets of our EC2 instance to our GitHub repository to make this GitHub Action work. Otherwise, it will not know where our server is. To add the secrets go to your repository, and follow the path: <code>settings</code> &gt; <code>secrets</code> &gt; <code>actions</code>. Then, click on <code>New Repository Secret</code> and add the following secrets:</p>
<ul>
<li><code>REMOTE_HOST</code>: the IP address of your remote host</li>
<li><code>SSH_PRIVATE_KEY</code>: the private key needed to ssh into the server</li>
<li><code>SSH_USERNAME</code>: the username of the user on the remote machine.</li>
</ul>
<p>Finally, commit and push all your changes and observe the GitHub action running in GitHub. It should run and end successfully.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thank you for reading! The github repository for containing the blog is: <a target="_blank" href="https://github.com/Reinforz/platform-status-blog">github.com/Reinforz/platform-status-blog</a>. If you have any questions or face any problems while doing the steps written in this blog, don't hesitate to comment down below. Also, check out our website at <a target="_blank" href="https://www.reinforz.ai/">reinforz.ai</a>.</p>
<div>
Written by <a href="https://github.com/imoxto">Rafid Hamid</a>
</div>
]]></content:encoded></item><item><title><![CDATA[Implementing Authentication with Twitter Oauth 2.0 using Typescript, Node js, Express js, and Next js in a Full Stack Application]]></title><description><![CDATA[What will we learn
Here, we will learn to implement authentication using Twitter OAuth 2.0 on a minimal working full-stack web application. We will not be using Passport.js or similar libraries to handle authentication for us. As a result, we will un...]]></description><link>https://blog.reinforz.ai/implementing-authentication-with-twitter-oauth-20-using-typescript-node-js-express-js-and-next-js-in-a-full-stack-application</link><guid isPermaLink="true">https://blog.reinforz.ai/implementing-authentication-with-twitter-oauth-20-using-typescript-node-js-express-js-and-next-js-in-a-full-stack-application</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[OAuth2]]></category><category><![CDATA[Twitter]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Reinforz AI]]></dc:creator><pubDate>Fri, 21 Oct 2022 11:40:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/k1xf2D7jWUs/upload/v1666351855441/vHUW329h9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-will-we-learn">What will we learn</h2>
<p>Here, we will learn to implement authentication using Twitter OAuth 2.0 on a minimal working full-stack web application. We will not be using Passport.js or similar libraries to handle authentication for us. As a result, we will understand the OAuth 2.0 flow better. We will also learn about the following stacks:</p>
<ul>
<li><a target="_blank" href="https://expressjs.com/">express.js</a> backend framework</li>
<li><a target="_blank" href="https://www.prisma.io/">prisma</a> to create and login users, you can really use anything to communicate with any database.</li>
<li><a target="_blank" href="https://nextjs.org/">next.js</a>, a <a target="_blank" href="https://reactjs.org/">React.js</a> framework, for the frontend</li>
<li><a target="_blank" href="https://www.typescriptlang.org/">typescript</a> (optional) type-safety for javascript</li>
</ul>
<h2 id="heading-requirements">Requirements</h2>
<p>Anyone with a basic knowledge of javascript can follow along with this blog.
If you already have a similar project setup, you can also jump straight to the <a class="post-section-overview" href="#twitter-oauth2-implementation">Twitter OAuth2 Implementation</a> section.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Firstly, let's add a <code>package.json</code> file at the root directory and add the following content:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"workspaces"</span>: [
    <span class="hljs-string">"server"</span>,
    <span class="hljs-string">"client"</span>
  ],
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"client:dev"</span>: <span class="hljs-string">"yarn workspace client dev"</span>,
    <span class="hljs-attr">"server:dev"</span>: <span class="hljs-string">"yarn workspace server dev"</span>,
    <span class="hljs-attr">"client:add"</span>: <span class="hljs-string">"yarn workspace client add"</span>,
    <span class="hljs-attr">"server:add"</span>: <span class="hljs-string">"yarn workspace server add"</span>,
    <span class="hljs-attr">"migrate-db"</span>: <span class="hljs-string">"yarn workspace server prisma-migrate"</span>
  }
}
</code></pre>
<p>You can set up version control in this directory, but that is optional. Either way, we will now add a client and server for our web app.</p>
<h3 id="heading-client-setup">Client setup</h3>
<p>Make a Next.js app by running the following commands:</p>
<pre><code class="lang-bash">yarn create next-app --typescript client
</code></pre>
<p>Skip the <code>--typescript</code> flag if you want to work with javascript.</p>
<p>This will create a <code>client</code> folder in the project directory. Navigate there and delete the files we don't need, i.e. <code>client\styles\Home.module.css</code> and <code>client\pages\api</code>. Also, let's replace all the code in <code>client\pages\index.ts</code> with the following:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { NextPage } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;

<span class="hljs-keyword">const</span> Home: NextPage = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"column-container"</span>&gt;
      &lt;p&gt;Hello!&lt;/p&gt;
    &lt;/div&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Home;
</code></pre>
<p>Starting the client with our command <code>yarn client:dev</code> and going to the address at http://www.localhost:3000/ should display a webpage saying <code>Hello!</code></p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/1.png" /></p>
<p>Now that the frontend is set up, let's move on to our backend.</p>
<h3 id="heading-server-setup">Server setup</h3>
<p>Make a directory called <code>server</code> and make a <code>package.json</code> file in the project directory with the following content:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"server"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node dist/index.js"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc --sourceMap false"</span>,
    <span class="hljs-attr">"build:watch"</span>: <span class="hljs-string">"tsc -w"</span>,
    <span class="hljs-attr">"start:watch"</span>: <span class="hljs-string">"nodemon dist/index.js"</span>,
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"concurrently \"yarn build:watch\" \"yarn start:watch\" --names \"tsc,node\" -c \"blue,green\""</span>,
    <span class="hljs-attr">"prisma-migrate"</span>: <span class="hljs-string">"prisma migrate dev"</span>,
    <span class="hljs-attr">"prisma-gen"</span>: <span class="hljs-string">"prisma generate"</span>
  }
}
</code></pre>
<p>Here, we added various scripts to help us in our development stage. We will mostly use the <code>dev</code> and the <code>migrate-db</code> scripts. They allow us to start the server in watch mode and let us migrate the database respectively. Now we can return to the workspace directory and use our <code>yarn server:add</code> to add packages. So its time to install the required dependencies using the following commands in the terminal:</p>
<pre><code class="lang-bash">yarn server:add @prisma/client argon2 axios cookie-parser cors dotenv express jsonwebtoken
</code></pre>
<pre><code class="lang-bash">yarn server:add -D nodemon prisma typescript concurrently @types/cookie-parser @types/cors @types/express @types/jsonwebtoken @types/node
</code></pre>
<p>After installing the dependencies we need, make a few files to have a minimal running express server:</p>
<ul>
<li><code>server/tsconfig.json</code> Edit according to preferences or skip if not using typescript<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ES2018"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"commonjs"</span>,
    <span class="hljs-attr">"lib"</span>: [
      <span class="hljs-string">"esnext"</span>, <span class="hljs-string">"esnext.asynciterable"</span>
    ],
    <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"sourceMap"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"declaration"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"node"</span>,
    <span class="hljs-attr">"noImplicitAny"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"strictNullChecks"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"strictFunctionTypes"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"noImplicitThis"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"noUnusedLocals"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"noUnusedParameters"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"noImplicitReturns"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"noFallthroughCasesInSwitch"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"allowSyntheticDefaultImports"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"emitDecoratorMetadata"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"experimentalDecorators"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"resolveJsonModule"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"incremental"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"baseUrl"</span>: <span class="hljs-string">"./src"</span>,
    <span class="hljs-attr">"watch"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"removeComments"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"./dist"</span>,
    <span class="hljs-attr">"rootDir"</span>: <span class="hljs-string">"./src"</span>
  },
  <span class="hljs-attr">"types"</span>: [<span class="hljs-string">"node"</span>],
  <span class="hljs-attr">"include"</span>: [<span class="hljs-string">"./src/**/*.ts"</span>],
}
</code></pre>
</li>
<li><p><code>server/src/index.ts</code> to listen to the server.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CLIENT_URL, SERVER_PORT } <span class="hljs-keyword">from</span> <span class="hljs-string">"./config"</span>;
<span class="hljs-keyword">import</span> cookieParser <span class="hljs-keyword">from</span> <span class="hljs-string">"cookie-parser"</span>;
<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">"cors"</span>;
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;

<span class="hljs-keyword">const</span> app = express();

<span class="hljs-keyword">const</span> origin = [CLIENT_URL];

app.use(cookieParser());
app.use(cors({
  origin,
  credentials: <span class="hljs-literal">true</span>
}))

app.get(<span class="hljs-string">"/ping"</span>, <span class="hljs-function">(<span class="hljs-params">_, res</span>) =&gt;</span> res.json(<span class="hljs-string">"pong"</span>));

app.listen(SERVER_PORT, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server listening on port <span class="hljs-subst">${SERVER_PORT}</span>`</span>))
</code></pre>
</li>
<li><p><code>server/src/config.ts</code> to export some constant configuration variables</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { PrismaClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@prisma/client"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CLIENT_URL = process.env.CLIENT_URL!
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SERVER_PORT = process.env.SERVER_PORT!

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> prisma = <span class="hljs-keyword">new</span> PrismaClient()
</code></pre>
</li>
<li><code>server/.env</code> to setup the port, client URL and database URL for the server. Make sure to ignore this file if you are working with version control<pre><code class="lang-dotenv">DATABASE_URL=postgres://postgres:postgres@localhost:5432/twitter-oauth2
CLIENT_URL=http://www.localhost:3000
SERVER_PORT=3001
</code></pre>
</li>
<li><p><code>server/prisma/schema.prisma</code> to let prisma handle the database structure</p>
<pre><code class="lang-prisma">generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

enum UserType {
  local
  twitter
}

model User {
  id       String   @id @default(uuid())
  name     String
  username String   @unique
  type     UserType @default(local)
}
</code></pre>
<p>Now migrate the database using the <code>yarn migrate-db</code> command, and then we can run the server using <code>yarn server:dev</code>.</p>
</li>
</ul>
<p>We should now be able to ping our server at http://localhost:3001/ping</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/2.png" /></p>
<h2 id="heading-twitter-oauth2-implementation">Twitter OAuth2 Implementation</h2>
<p>We are ready to implement authentication via Twitter OAuth 2.0 into our app. We will follow <a target="_blank" href="https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code">this</a> approach to do so. 
Firstly, we have to make an app on Twitter.</p>
<h3 id="heading-setup-twitter-user-authentication-settings">Setup twitter user authentication settings</h3>
<p>Head over to <a target="_blank" href="https://developer.twitter.com/en/portal/dashboard">twitter's developer portal</a> and make a project and a development app in the project with any name. Twitter will show you the things needed. It may take a few hours to get approval from Twitter to make these apps. Once it is done, head over to the settings page of the app to set some necessary fields.
Set up or edit the user authentication as needed by your app.</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/3.png" /></p>
<p>As I only need to read profile information for this minimal web app, these are the settings I used:</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/4.png" /></p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/5.png" /></p>
<p>Save the Twitter Client ID and client secret securely.</p>
<blockquote>
<p><strong>Note</strong>: http://www.localhost:3000 works but not http://localhost:3000. 
So, I added <code>www.</code> in both websites.</p>
</blockquote>
<h3 id="heading-client">Client</h3>
<h4 id="heading-frontend-authentication-button">Frontend authentication button</h4>
<p>Now we add the button in the client, which will lead to our backend for authentication.
To do so, we need to use a valid Twitter OAuth URL getter function and a button to go to the URL.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> twitterIcon <span class="hljs-keyword">from</span> <span class="hljs-string">"../public/twitter.svg"</span>;
<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</span>;

<span class="hljs-keyword">const</span> TWITTER_CLIENT_ID = <span class="hljs-string">"T1dLaHdFSWVfTnEtQ2psZThTbnI6MTpjaQ"</span> <span class="hljs-comment">// give your twitter client id here</span>

<span class="hljs-comment">// twitter oauth Url constructor</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTwitterOauthUrl</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> rootUrl = <span class="hljs-string">"https://twitter.com/i/oauth2/authorize"</span>;
  <span class="hljs-keyword">const</span> options = {
    redirect_uri: <span class="hljs-string">"http://www.localhost:3001/oauth/twitter"</span>, <span class="hljs-comment">// client url cannot be http://localhost:3000/ or http://127.0.0.1:3000/</span>
    client_id: TWITTER_CLIENT_ID,
    state: <span class="hljs-string">"state"</span>,
    response_type: <span class="hljs-string">"code"</span>,
    code_challenge: <span class="hljs-string">"y_SfRG4BmOES02uqWeIkIgLQAlTBggyf_G7uKT51ku8"</span>,
    code_challenge_method: <span class="hljs-string">"S256"</span>,
    scope: [<span class="hljs-string">"users.read"</span>, <span class="hljs-string">"tweet.read"</span>, <span class="hljs-string">"follows.read"</span>, <span class="hljs-string">"follows.write"</span>].join(<span class="hljs-string">" "</span>), <span class="hljs-comment">// add/remove scopes as needed</span>
  };
  <span class="hljs-keyword">const</span> qs = <span class="hljs-keyword">new</span> URLSearchParams(options).toString();
  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${rootUrl}</span>?<span class="hljs-subst">${qs}</span>`</span>;
}

<span class="hljs-comment">// the component</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TwitterOauthButton</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;a className=<span class="hljs-string">"a-button row-container"</span> href={getTwitterOauthUrl()}&gt;
      &lt;Image src={twitterIcon} alt=<span class="hljs-string">"twitter icon"</span> /&gt;
      &lt;p&gt;{<span class="hljs-string">" twitter"</span>}&lt;/p&gt;
    &lt;/a&gt;
  );
}
</code></pre>
<blockquote>
<p><strong>Note</strong>: We are hard coding <code>code_challenge</code> and <code>code_verifier</code> for simplicity. You can randomly generate it.</p>
</blockquote>
<p>After adding the above code in <code>client\components\TwitterOauthButton.tsx</code>, we will add a twitter SVG icon (from online resources like <a target="_blank" href="https://icons8.com/icons/set/twitter">this</a>) on path <code>client\public\twitter.svg</code>.
Then we will import the component on the homepage:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { TwitterOauthButton } <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/TwitterOauthButton"</span>;

<span class="hljs-keyword">const</span> Home: NextPage = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"column-container"</span>&gt;
      &lt;p&gt;Hello!&lt;/p&gt;
      &lt;TwitterOauthButton /&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>This is how it should look like afterwards:</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/6.png" /></p>
<p>Clicking on the Twitter icon will lead us to the Twitter page where we can authorize the app:</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/6.5.png" /></p>
<p>Of course, clicking on the <code>authorize app</code> button leads to a <code>Cannot GET /oauth/twitter</code> response, as we haven't implemented the backend yet.</p>
<h4 id="heading-me-query">Me query</h4>
<p>Let's request for the current logged in user from the frontend through a hook, <code>client\hooks\useMeQuery.ts</code>:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> axios, { AxiosResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> User = {
  id: <span class="hljs-built_in">string</span>;
  name: <span class="hljs-built_in">string</span>;
  username: <span class="hljs-built_in">string</span>;
  <span class="hljs-keyword">type</span>: <span class="hljs-string">"local"</span> | <span class="hljs-string">"twitter"</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useMeQuery</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [error, setError] = useState&lt;<span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [data, setData] = useState&lt;User | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    setLoading(<span class="hljs-literal">true</span>);
    axios
      .get&lt;<span class="hljs-built_in">any</span>, AxiosResponse&lt;User&gt;&gt;(<span class="hljs-string">`http://www.localhost:3001/me`</span>, {
        withCredentials: <span class="hljs-literal">true</span>,
      })
      .then(<span class="hljs-function">(<span class="hljs-params">v</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (v.data) setData(v.data);
      })
      .catch(<span class="hljs-function">() =&gt;</span> setError(<span class="hljs-string">"Not Authenticated"</span>))
      .finally(<span class="hljs-function">() =&gt;</span> setLoading(<span class="hljs-literal">false</span>));
  }, []);

  <span class="hljs-keyword">return</span> { error, data, loading };
}
</code></pre>
<p>This will do a good enough job for our minimal app. We will use it to determine what to render. We will render the username if we get a user from the hook. Otherwise, we will render the <code>Login with Twitter</code> button</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { NextPage } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;
<span class="hljs-keyword">import</span> { TwitterOauthButton } <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/TwitterOauthButton"</span>;
<span class="hljs-keyword">import</span> { useMeQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useMeQuery"</span>;

<span class="hljs-keyword">const</span> Home: NextPage = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { data: user } = useMeQuery();
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"column-container"</span>&gt;
      &lt;p&gt;Hello!&lt;/p&gt;
      {user ? (<span class="hljs-comment">// user present so only display user's name</span>
        &lt;p&gt;{user.name}&lt;/p&gt;
      ) : (<span class="hljs-comment">// user not present so prompt to login</span>
        &lt;div&gt;
          &lt;p&gt;You are not Logged <span class="hljs-keyword">in</span>! Login <span class="hljs-keyword">with</span>:&lt;/p&gt;
          &lt;TwitterOauthButton /&gt;
        &lt;/div&gt;
      )}
    &lt;/div&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Home;
</code></pre>
<p>The above is how the final <code>client\pages\index.tsx</code> will look like. Go to http://www.localhost:3000 and inspect the network window of the browser while the page is loading. You should see the Me query being executed there.</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/6.6.png" /></p>
<blockquote>
<p>Its 404 because we havent implemented it in the backend</p>
</blockquote>
<h4 id="heading-styling">Styling</h4>
<p>Let's just add some basic styling while we are at it by modifying the <code>client\styles\globals.css</code> file:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span>,
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-family</span>: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

<span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">color</span>: inherit;
  <span class="hljs-attribute">text-decoration</span>: none;
}

<span class="hljs-selector-class">.column-container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-class">.row-container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: row;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-class">.a-button</span> {
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid grey;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
}

<span class="hljs-selector-class">.a-button</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#111133</span>;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-color-scheme:</span> dark) {
  <span class="hljs-selector-tag">html</span> {
    <span class="hljs-attribute">color-scheme</span>: dark;
  }
  <span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: black;
  }
}
</code></pre>
<p>That is all we have to do on our client-side. The final homepage should look like this:</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/7.png" /></p>
<h3 id="heading-server">Server</h3>
<p>As we saw from our frontend, we need to implement <code>GET /oauth/twitter</code> route in our server to make the Twitter OAuth part of the app work. A look at the <a target="_blank" href="https://developer.twitter.com/en/docs/authentication/oauth-2-0/user-access-token">twitter documentation</a> reveals the steps we need to perform so that we can read the info we mentioned in our scopes <a class="post-section-overview" href="#frontend-authentication-button">here</a>.
These steps are summarized below:</p>
<ol>
<li>getting the access token</li>
<li>getting the Twitter user from the access token</li>
<li>upsert the user in our database</li>
<li>create cookie so that the server can validate the user</li>
<li>redirect to the client with the cookie</li>
</ol>
<blockquote>
<p><strong>Note</strong>: Only the first two steps are related to Twitter OAuth Implementation</p>
</blockquote>
<p>Lets add a file <code>server\src\oauth2.ts</code> where we will add our OAuth related codes. We will complete the steps above by defining a function there:</p>
<pre><code class="lang-ts"><span class="hljs-comment">// the function which will be called when twitter redirects to the server at http://www.localhost:3001/oauth/twitter</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">twitterOauth</span>(<span class="hljs-params">req: Request&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>, {code:<span class="hljs-built_in">string</span>}&gt;, res: Response</span>) </span>{
  <span class="hljs-keyword">const</span> code = req.query.code; <span class="hljs-comment">// getting the code if the user authorized the app</span>

  <span class="hljs-comment">// 1. get the access token with the code</span>

  <span class="hljs-comment">// 2. get the twitter user using the access token</span>

  <span class="hljs-comment">// 3. upsert the user in our db</span>

  <span class="hljs-comment">// 4. create cookie so that the server can validate the user</span>

  <span class="hljs-comment">// 5. finally redirect to the client</span>

  <span class="hljs-keyword">return</span> res.redirect(CLIENT_URL);
}
</code></pre>
<p>Before doing any of that make sure we the Twitter OAuth client secret in our <code>.env</code> file. We will also add a JWT secret there so that we can encrypt the cookie we send to the client. The final <code>.env</code> file should look like this:</p>
<pre><code class="lang-env">DATABASE_URL=postgres://postgres:postgres@localhost:5432/twitter-oauth2
CLIENT_URL=http://www.localhost:3000
SERVER_PORT=3001
JWT_SECRET=put-your-jwt-secret-here
TWITTER_CLIENT_SECRET=put-your-twitter-client-secret-here
</code></pre>
<h4 id="heading-getting-the-access-token-with-the-code">Getting the access token with the code</h4>
<p>Add the following code(the commented lines explain what they do) to get the access token:</p>
<pre><code class="lang-ts"><span class="hljs-comment">// add your client id and secret here:</span>
<span class="hljs-keyword">const</span> TWITTER_OAUTH_CLIENT_ID = <span class="hljs-string">"T1dLaHdFSWVfTnEtQ2psZThTbnI6MTpjaQ"</span>;
<span class="hljs-keyword">const</span> TWITTER_OAUTH_CLIENT_SECRET = process.env.TWITTER_CLIENT_SECRET!;

<span class="hljs-comment">// the url where we get the twitter access token from</span>
<span class="hljs-keyword">const</span> TWITTER_OAUTH_TOKEN_URL = <span class="hljs-string">"https://api.twitter.com/2/oauth2/token"</span>;

<span class="hljs-comment">// we need to encrypt our twitter client id and secret here in base 64 (stated in twitter documentation)</span>
<span class="hljs-keyword">const</span> BasicAuthToken = Buffer.from(<span class="hljs-string">`<span class="hljs-subst">${TWITTER_OAUTH_CLIENT_ID}</span>:<span class="hljs-subst">${TWITTER_OAUTH_CLIENT_SECRET}</span>`</span>, <span class="hljs-string">"utf8"</span>).toString(
  <span class="hljs-string">"base64"</span>
);

<span class="hljs-comment">// filling up the query parameters needed to request for getting the token</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> twitterOauthTokenParams = {
  client_id: TWITTER_OAUTH_CLIENT_ID,
  <span class="hljs-comment">// based on code_challenge</span>
  code_verifier: <span class="hljs-string">"8KxxO-RPl0bLSxX5AWwgdiFbMnry_VOKzFeIlVA7NoA"</span>,
  redirect_uri: <span class="hljs-string">`http://www.localhost:3001/oauth/twitter`</span>,
  grant_type: <span class="hljs-string">"authorization_code"</span>,
};

<span class="hljs-comment">// the shape of the object we should recieve from twitter in the request</span>
<span class="hljs-keyword">type</span> TwitterTokenResponse = {
  token_type: <span class="hljs-string">"bearer"</span>;
  expires_in: <span class="hljs-number">7200</span>;
  access_token: <span class="hljs-built_in">string</span>;
  scope: <span class="hljs-built_in">string</span>;
};

<span class="hljs-comment">// the main step 1 function, getting the access token from twitter using the code that twitter sent us</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTwitterOAuthToken</span>(<span class="hljs-params">code: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// POST request to the token url to get the access token</span>
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.post&lt;TwitterTokenResponse&gt;(
      TWITTER_OAUTH_TOKEN_URL,
      <span class="hljs-keyword">new</span> URLSearchParams({ ...twitterOauthTokenParams, code }).toString(),
      {
        headers: {
          <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/x-www-form-urlencoded"</span>,
          Authorization: <span class="hljs-string">`Basic <span class="hljs-subst">${BasicAuthToken}</span>`</span>,
        },
      }
    );

    <span class="hljs-keyword">return</span> res.data;
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(err);

    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }
}
</code></pre>
<h4 id="heading-getting-the-twitter-user-from-access-token">Getting the Twitter User from access token</h4>
<p>Similar code to get user from access token:</p>
<pre><code class="lang-ts"><span class="hljs-comment">// the shape of the response we should get</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> TwitterUser {
  id: <span class="hljs-built_in">string</span>;
  name: <span class="hljs-built_in">string</span>;
  username: <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// getting the twitter user from access token</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTwitterUser</span>(<span class="hljs-params">accessToken: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">TwitterUser</span> | <span class="hljs-title">null</span>&gt; </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// request GET https://api.twitter.com/2/users/me</span>
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get&lt;{ data: TwitterUser }&gt;(<span class="hljs-string">"https://api.twitter.com/2/users/me"</span>, {
      headers: {
        <span class="hljs-string">"Content-type"</span>: <span class="hljs-string">"application/json"</span>,
        <span class="hljs-comment">// put the access token in the Authorization Bearer token</span>
        Authorization: <span class="hljs-string">`Bearer <span class="hljs-subst">${accessToken}</span>`</span>,
      },
    });

    <span class="hljs-keyword">return</span> res.data.data ?? <span class="hljs-literal">null</span>;
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(err);

    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }
}
</code></pre>
<h4 id="heading-checking-if-they-work">Checking if they work</h4>
<p>Let's see if they successfully gets us the user. After adding all the code in the <code>server\src\oauth2.ts</code> file it should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CLIENT_URL } <span class="hljs-keyword">from</span> <span class="hljs-string">"./config"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> { Request, Response } <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;

<span class="hljs-comment">// add your client id and secret here:</span>
<span class="hljs-keyword">const</span> TWITTER_OAUTH_CLIENT_ID = <span class="hljs-string">"T1dLaHdFSWVfTnEtQ2psZThTbnI6MTpjaQ"</span>;
<span class="hljs-keyword">const</span> TWITTER_OAUTH_CLIENT_SECRET = process.env.TWITTER_CLIENT_SECRET!;

<span class="hljs-comment">// the url where we get the twitter access token from</span>
<span class="hljs-keyword">const</span> TWITTER_OAUTH_TOKEN_URL = <span class="hljs-string">"https://api.twitter.com/2/oauth2/token"</span>;

<span class="hljs-comment">// we need to encrypt our twitter client id and secret here in base 64 (stated in twitter documentation)</span>
<span class="hljs-keyword">const</span> BasicAuthToken = Buffer.from(<span class="hljs-string">`<span class="hljs-subst">${TWITTER_OAUTH_CLIENT_ID}</span>:<span class="hljs-subst">${TWITTER_OAUTH_CLIENT_SECRET}</span>`</span>, <span class="hljs-string">"utf8"</span>).toString(
  <span class="hljs-string">"base64"</span>
);

<span class="hljs-comment">// filling up the query parameters needed to request for getting the token</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> twitterOauthTokenParams = {
  client_id: TWITTER_OAUTH_CLIENT_ID,
  code_verifier: <span class="hljs-string">"8KxxO-RPl0bLSxX5AWwgdiFbMnry_VOKzFeIlVA7NoA"</span>,
  redirect_uri: <span class="hljs-string">`http://www.localhost:3001/oauth/twitter`</span>,
  grant_type: <span class="hljs-string">"authorization_code"</span>,
};

<span class="hljs-comment">// the shape of the object we should recieve from twitter in the request</span>
<span class="hljs-keyword">type</span> TwitterTokenResponse = {
  token_type: <span class="hljs-string">"bearer"</span>;
  expires_in: <span class="hljs-number">7200</span>;
  access_token: <span class="hljs-built_in">string</span>;
  scope: <span class="hljs-built_in">string</span>;
};

<span class="hljs-comment">// the main step 1 function, getting the access token from twitter using the code that the twitter sent us</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTwitterOAuthToken</span>(<span class="hljs-params">code: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// POST request to the token url to get the access token</span>
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.post&lt;TwitterTokenResponse&gt;(
      TWITTER_OAUTH_TOKEN_URL,
      <span class="hljs-keyword">new</span> URLSearchParams({ ...twitterOauthTokenParams, code }).toString(),
      {
        headers: {
          <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/x-www-form-urlencoded"</span>,
          Authorization: <span class="hljs-string">`Basic <span class="hljs-subst">${BasicAuthToken}</span>`</span>,
        },
      }
    );

    <span class="hljs-keyword">return</span> res.data;
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }
}

<span class="hljs-comment">// the shape of the response we should get</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> TwitterUser {
  id: <span class="hljs-built_in">string</span>;
  name: <span class="hljs-built_in">string</span>;
  username: <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// getting the twitter user from access token</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTwitterUser</span>(<span class="hljs-params">accessToken: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">TwitterUser</span> | <span class="hljs-title">null</span>&gt; </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// request GET https://api.twitter.com/2/users/me</span>
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get&lt;{ data: TwitterUser }&gt;(<span class="hljs-string">"https://api.twitter.com/2/users/me"</span>, {
      headers: {
        <span class="hljs-string">"Content-type"</span>: <span class="hljs-string">"application/json"</span>,
        <span class="hljs-comment">// put the access token in the Authorization Bearer token</span>
        Authorization: <span class="hljs-string">`Bearer <span class="hljs-subst">${accessToken}</span>`</span>,
      },
    });

    <span class="hljs-keyword">return</span> res.data.data ?? <span class="hljs-literal">null</span>;
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }
}

<span class="hljs-comment">// the function which will be called when twitter redirects to the server at http://www.localhost:3001/oauth/twitter</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">twitterOauth</span>(<span class="hljs-params">req: Request&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>, {code:<span class="hljs-built_in">string</span>}&gt;, res: Response</span>) </span>{
  <span class="hljs-keyword">const</span> code = req.query.code;

  <span class="hljs-comment">// 1. get the access token with the code</span>
  <span class="hljs-keyword">const</span> TwitterOAuthToken = <span class="hljs-keyword">await</span> getTwitterOAuthToken(code);
  <span class="hljs-built_in">console</span>.log(TwitterOAuthToken);

  <span class="hljs-keyword">if</span> (!TwitterOAuthToken) {
    <span class="hljs-comment">// redirect if no auth token</span>
    <span class="hljs-keyword">return</span> res.redirect(CLIENT_URL);
  }

  <span class="hljs-comment">// 2. get the twitter user using the access token</span>
  <span class="hljs-keyword">const</span> twitterUser = <span class="hljs-keyword">await</span> getTwitterUser(TwitterOAuthToken.access_token);
  <span class="hljs-built_in">console</span>.log(twitterUser);

  <span class="hljs-keyword">if</span> (!twitterUser) {
    <span class="hljs-comment">// redirect if no twitter user</span>
    <span class="hljs-keyword">return</span> res.redirect(CLIENT_URL);
  }

  <span class="hljs-comment">// 3. upsert the user in our db</span>

  <span class="hljs-comment">// 4. create cookie so that the server can validate the user</span>

  <span class="hljs-comment">// 5. finally redirect to the client</span>

  <span class="hljs-keyword">return</span> res.redirect(CLIENT_URL);
}
</code></pre>
<p>Import and add the route to our express app:</p>
<pre><code class="lang-ts">app.get(<span class="hljs-string">"/ping"</span>, <span class="hljs-function">(<span class="hljs-params">_, res</span>) =&gt;</span> res.json(<span class="hljs-string">"pong"</span>));

<span class="hljs-comment">// activate twitterOauth function when visiting the route </span>
app.get(<span class="hljs-string">"/oauth/twitter"</span>, twitterOauth);
app.listen(SERVER_PORT, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server listening on port <span class="hljs-subst">${SERVER_PORT}</span>`</span>))
</code></pre>
<p>Now run the client and server, and look at the server console on what happens if we click on the Twitter button in the frontend and authorize the app.</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/8.png" /></p>
<p>We successfully got the user from Twitter now!
The most important part, i.e. getting the user from Twitter, is done. Now we can finish up our project.</p>
<h2 id="heading-finishing-the-web-app">Finishing the web app</h2>
<p>Let's finish up the rest of the steps needed for <code>GET /oauth/twitter</code> to work. Since they are not related to OAuth, I will add the functions in the <code>server\src\config.ts</code> file.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { PrismaClient, User } <span class="hljs-keyword">from</span> <span class="hljs-string">"@prisma/client"</span>
<span class="hljs-keyword">import</span> { CookieOptions, Response } <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> { TwitterUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"./oauth2"</span>;
<span class="hljs-keyword">import</span> jwt <span class="hljs-keyword">from</span> <span class="hljs-string">"jsonwebtoken"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CLIENT_URL = process.env.CLIENT_URL!
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SERVER_PORT = process.env.SERVER_PORT!
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> prisma = <span class="hljs-keyword">new</span> PrismaClient()

<span class="hljs-comment">// step 3</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">upsertUser</span>(<span class="hljs-params">twitterUser: TwitterUser</span>) </span>{
  <span class="hljs-comment">// create a new user in our database or return an old user who already signed up earlier </span>
  <span class="hljs-keyword">return</span> prisma.user.upsert({
    create: {
      username: twitterUser.username,
      id: twitterUser.id,
      name: twitterUser.name,
      <span class="hljs-keyword">type</span>: <span class="hljs-string">"twitter"</span>,
    },
    update: {
      id: twitterUser.id,
    },
    where: {  id: twitterUser.id},
  });
}

<span class="hljs-comment">// JWT_SECRET from our environment variable file</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> JWT_SECRET = process.env.JWT_SECRET!

<span class="hljs-comment">// cookie name</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> COOKIE_NAME = <span class="hljs-string">'oauth2_token'</span>

<span class="hljs-comment">// cookie setting options</span>
<span class="hljs-keyword">const</span> cookieOptions: CookieOptions = {
  httpOnly: <span class="hljs-literal">true</span>,
  secure: process.env.NODE_ENV === <span class="hljs-string">'production'</span>
  sameSite: <span class="hljs-string">"strict"</span>
}

<span class="hljs-comment">// step 4</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addCookieToRes</span>(<span class="hljs-params">res: Response, user: User, accessToken: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">const</span> { id, <span class="hljs-keyword">type</span> } = user;
  <span class="hljs-keyword">const</span> token = jwt.sign({ <span class="hljs-comment">// Signing the token to send to client side</span>
    id,
    accessToken,
    <span class="hljs-keyword">type</span>
  }, JWT_SECRET);
  res.cookie(COOKIE_NAME, token, {  <span class="hljs-comment">// adding the cookie to response here</span>
    ...cookieOptions,
    expires: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-built_in">Date</span>.now() + <span class="hljs-number">7200</span> * <span class="hljs-number">1000</span>),
  });
}
</code></pre>
<p>Import the functions and use them in the <code>server\src\oauth2.ts</code>:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { prisma, CLIENT_URL, addResCookie } <span class="hljs-keyword">from</span> <span class="hljs-string">"./config"</span>;

...

<span class="hljs-comment">// the function which will be called when twitter redirects to the server at http://www.localhost:3001/oauth/twitter</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">twitterOauth</span>(<span class="hljs-params">req: Request&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>, {code:<span class="hljs-built_in">string</span>}&gt;, res: Response</span>) </span>{
  <span class="hljs-keyword">const</span> code = req.query.code;

  <span class="hljs-comment">// 1. get the access token with the code</span>
  <span class="hljs-keyword">const</span> twitterOAuthToken = <span class="hljs-keyword">await</span> getTwitterOAuthToken(code);

  <span class="hljs-keyword">if</span> (!twitterOAuthToken) {
    <span class="hljs-comment">// redirect if no auth token</span>
    <span class="hljs-keyword">return</span> res.redirect(CLIENT_URL);
  }

  <span class="hljs-comment">// 2. get the twitter user using the access token</span>
  <span class="hljs-keyword">const</span> twitterUser = <span class="hljs-keyword">await</span> getTwitterUser(twitterOAuthToken.access_token);

  <span class="hljs-keyword">if</span> (!twitterUser) {
    <span class="hljs-comment">// redirect if no twitter user</span>
    <span class="hljs-keyword">return</span> res.redirect(CLIENT_URL);
  }


  <span class="hljs-comment">// 3. upsert the user in our db</span>
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> upsertUser(twitterUser)

  <span class="hljs-comment">// 4. create cookie so that the server can validate the user</span>
  addCookieToRes(res, user, twitterOAuthToken.access_token)

  <span class="hljs-comment">// 5. finally redirect to the client</span>
  <span class="hljs-keyword">return</span> res.redirect(CLIENT_URL);
}
</code></pre>
<blockquote>
<p><strong>Note</strong>: We are sending the access token in the cookie for simplicity. For a web application, we should store it somewhere more secure, like a database.</p>
</blockquote>
<p>And finally, add the <code>me</code> query in the <code>server\src\index.ts</code> file.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CLIENT_URL, COOKIE_NAME, JWT_SECRET, prisma, SERVER_PORT } <span class="hljs-keyword">from</span> <span class="hljs-string">"./config"</span>;
<span class="hljs-keyword">import</span> cookieParser <span class="hljs-keyword">from</span> <span class="hljs-string">"cookie-parser"</span>;
<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">"cors"</span>;
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> jwt <span class="hljs-keyword">from</span> <span class="hljs-string">'jsonwebtoken'</span>
<span class="hljs-keyword">import</span> { getTwitterUser, twitterOauth } <span class="hljs-keyword">from</span> <span class="hljs-string">"./oauth2"</span>;
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"@prisma/client"</span>;

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> origin= [CLIENT_URL];
app.use(cookieParser());
app.use(cors({
  origin,
  credentials: <span class="hljs-literal">true</span>
}))
app.get(<span class="hljs-string">"/ping"</span>, <span class="hljs-function">(<span class="hljs-params">_, res</span>) =&gt;</span> res.json(<span class="hljs-string">"pong"</span>));

<span class="hljs-keyword">type</span> UserJWTPayload = Pick&lt;User, <span class="hljs-string">'id'</span>|<span class="hljs-string">'type'</span>&gt; &amp; {accessToken: <span class="hljs-built_in">string</span>}

app.get(<span class="hljs-string">'/me'</span>, <span class="hljs-keyword">async</span> (req, res)=&gt;{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> token = req.cookies[COOKIE_NAME];
    <span class="hljs-keyword">if</span> (!token) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not Authenticated"</span>);
    }
    <span class="hljs-keyword">const</span> payload = <span class="hljs-keyword">await</span> jwt.verify(token, JWT_SECRET) <span class="hljs-keyword">as</span> UserJWTPayload;
    <span class="hljs-keyword">const</span> userFromDb = <span class="hljs-keyword">await</span> prisma.user.findUnique({
      where: { id: payload?.id },
    });
    <span class="hljs-keyword">if</span> (!userFromDb) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not Authenticated"</span>);
    <span class="hljs-keyword">if</span> (userFromDb.type === <span class="hljs-string">"twitter"</span>) {
      <span class="hljs-keyword">if</span> (!payload.accessToken) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not Authenticated"</span>);
      }
      <span class="hljs-keyword">const</span> twUser = <span class="hljs-keyword">await</span> getTwitterUser(payload.accessToken);
      <span class="hljs-keyword">if</span> (twUser?.id !== userFromDb.id) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not Authenticated"</span>);
      }
    }
    res.json(userFromDb)
  } <span class="hljs-keyword">catch</span> (err) {
    res.status(<span class="hljs-number">401</span>).json(<span class="hljs-string">"Not Authenticated"</span>)
  }
})

<span class="hljs-comment">// activate twitterOauth function when visiting the route </span>
app.get(<span class="hljs-string">"/oauth/twitter"</span>, twitterOauth);
app.listen(SERVER_PORT, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server listening on port <span class="hljs-subst">${SERVER_PORT}</span>`</span>))
</code></pre>
<p>It is done now! Let's see what happens when we click the Twitter button on our client and authorize the app there.</p>
<p><img src="https://raw.githubusercontent.com/Reinforz/twitter-oauth2-blog/main/images/9.png" /></p>
<p>We see our Twitter username in there instead of the Twitter button now, which shows that the <code>me</code> query is being executed successfully. As a result, we now have a working user authentication system, via Twitter OAuth 2.0, in our minimal full-stack web application. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks for reading! <a target="_blank" href="https://github.com/Reinforz/twitter-oauth2-blog">This</a> is the Github repository with all the codes. Find more fun things you can do with the Twitter API <a target="_blank" href="https://developer.twitter.com/en/docs/api-reference-index">here</a>. Another example implementation of authentication via Twitter OAuth 2.0 can be found <a target="_blank" href="https://github.com/imoxto/imodit">here</a>.</p>
<p><em>Written by <a target="_blank" href="https://github.com/imoxto">Rafid Hamid</a></em></p>
]]></content:encoded></item></channel></rss>