Understanding and Implementing OAuth2 In Node.js
Tags: node, javascript
Published: 2021-12-16 18:25:57

*This article was originally written by [Diogo Souza](https://www.honeybadger.io/blog/oauth-nodejs-javascript/#authorDetails) on the [Honeybadger Developer Blog](https://www.honeybadger.io/blog/oauth-nodejs-javascript/).*  

[OAuth 2.0](https://oauth.net/2/) is the most widely used and accepted industry-standard protocol for authorization. As a protocol, it provides a bunch of tools, patterns, and best practices to help developers with the arduous task of accomplishing authorization flows for all sorts of applications, such as web, mobile, and embedded.

In this article, I'll walk you through some of the essentials of OAuth2 in a practical way. The goal is to demonstrate how this famous protocol works behind the scenes, along with Node.js.

## So, What About OAuth2?

There are a lot of misconceptions and over-complications when ,it comes to understanding what OAuth is really about.

OAuth2 has a couple of predefined flows that you can use on your projects; however, some of them will not apply to your situation.

Here, we'll demonstrate a scenario in which the interested party who owns the web application in development (in this case, you) makes use of a third-party system to infer authorization within the application.

Among the third-party systems, you may consider the big players, such as Google, Facebook, or, in our case, GitHub.

![Authorization upon Facebook's systems](https://www.honeybadger.io/images/blog/posts/oauth-nodejs-javascript/Figure01.png)
_Authorization [upon Facebook's systems](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/#logindialog)._

Typically, OAuth2’s most commonly used flow is divided into three main steps:

1. Your application pops up a new window for the third-party authorization app, asking the user (who must have an account and be logged in on the system) to grant permission for your application and then be able to take actions on his/her behalf.
2. Once properly authenticated and authorized, the third-party app acknowledges the permission and redirects the user back to your app via a pre-configured URL.
3. Your application exposes an endpoint for such callback operations and hits the third-party provider API to ask for an access token based on the response _code_ returned by the previous redirect process.

If you don't want to delegate the process of keeping users’ login information and, instead, want to handle the authorization on your own, you can create an OAuth2 server.

This is what many companies do today due to the number of customizations required to meet their needs.

Let's look at this scenario more closely and see how Node.js deals with it.

## Environment Setup

For this article, we'll be using [Node.js](https://nodejs.org/en/), and [Yarn](https://yarnpkg.com/) as the default package manager. Node is on version v12.21.0 to be specific. However, most of the versions higher than 10 should work just fine.

Make sure to run each of the official installation steps to have them set up in case you still don't.

For this tutorial, we'll create two different applications: one for the client (in React) and another for the server capabilities.

It's important to know that this far, although you can perfectly build your OAuth2 server from scratch using frameworks, such as the [node-oauth2-server](https://github.com/oauthjs/node-oauth2-server), we'll make use of GitHub's capabilities for the sake of simplicity.

There are too many details around the ability to set up an OAuth2 server properly that we won't cover for now.

However, in case you'd like to adventure yourself into it, the server project we'll create will be more than adequate for the role of OAuth2's server host.

### GitHub App Config

For this tutorial, we'll make use of the powerful [GitHub OAuth2 API](https://docs.github.com/en/developers/apps/authorizing-oauth-apps). GitHub allows you to create an application under your account and provides you with some OAuth2 client credentials to use in other applications.

This way, you can easily allow users of your application to log in via their GitHub accounts.

So, let's create a new application by clicking this [link](https://github.com/settings/applications/new). Make sure to fill in all the fields, as shown in the image below:

![Registering a new OAuth application](https://www.honeybadger.io/images/blog/posts/oauth-nodejs-javascript/Figure02.png)
_Registering a new OAuth application._

The _Authorization callback URL_ is the most important field because it demarcates where GitHub should redirect the user to once the authorization process is finished.

Note that you can enter any URI you prefer. Click the _Register application_ button, and you may see a subsequent screen showing the _Client ID_ and your _Client secrets_.

![Client ID and secrets](https://www.honeybadger.io/images/blog/posts/oauth-nodejs-javascript/Figure03.png)
_Client ID and secrets._

Once you get there, you need to click the button to generate a new client secret. Make sure to copy/paste it to a safe place before leaving this screen since you won't be able to see it again in the future.

## The Server Project

Let's get back to project creation. Pick up a folder of your choice and run the following command:

```bash
mkdir oauth2-node-server
cd oauth2-node-server
npm init
```

Again, leave all the options as they're presented to you until the end. Then, run the following command to install the required NPM dependencies:

```bash
npm install axios express cors
```

_Axios_ will be used to make HTTP request calls to the GitHub OAuth2 servers. _Express_ will be our version of the server, and _cors_ is just used to avoid conflicts with the browser's _Same-Origin_ policies.

The code for the server is very simple and can be found in the following code. Make sure to add it to a file named _index.js_:

```javascript
const express = require("express");
const axios = require("axios");
var cors = require("cors");

const CLIENT_ID = "<YOUR GITHUB CLIENT ID>";
const CLIENT_SECRET = "<YOUR GITHUB CLIENT SECRET>";
const GITHUB_URL = "https://github.com/login/oauth/access_token";

const app = express();
app.use(cors({ credentials: true, origin: true }));

app.get("/oauth/redirect", (req, res) => {
  axios({
    method: "POST",
    url: `${GITHUB_URL}?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&code=${req.query.code}`,
    headers: {
      Accept: "application/json",
    },
  }).then((response) => {
    res.redirect(
      `http://localhost:3000?access_token=${response.data.access_token}`
    );
  });
});

const PORT = 8080;
app.listen(PORT, () => {
  console.log(`Listening at port ${PORT}`);
});
```

What we're doing on this server is simply proxying requests from the GitHub platform to the `/oauth/redirect` path once the authorization steps are concluded.

When it's done, we need to make sure to call it again to retrieve a valid access token. To do this, GitHub needs to know who's calling to check whether the caller has such access.

The only way GitHub can know this is via the _client id_ and _client secret_ provided by your GitHub application, so they may be passed as parameters to the request.

Also, note that we're sending a query param called `code` that GitHub provides when calling the callback URL as a second security check of its own. Otherwise, the request would fail.

If the second GitHub's call is successful, then we may redirect the response and all of its content to the React client application listening to port 3000.

Before heading to the next section, make sure everything’s working fine by running the following command:

```bash
node index.js
```

which may, in turn, produce the output shown below:

```text
➜ Listening at port 8080
```

## The Client Project

The client project will get some help from React and Bootstrap to make things aesthetically cleaner.

Leave the current server folder and run the following command to create the client project:

```bash
npx create-react-app oauth2-node-app
```

To make things simpler for the client, we're also making use of the [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) tool. After you run it, make sure to leave all the options as defaults until the end.

Then, run the following command to add the required Node dependencies:

```bash
yarn add react-bootstrap axios
```

Make sure to add this CSS import in your _index.js_ file to inject Bootstrap's styles into the React project:

```bash
import 'bootstrap/dist/css/bootstrap.min.css';
```

Great! Without further ado, let's move on and substitute your _App.js_ file content with the following:

```javascript
import Button from "react-bootstrap/Button";
import CardDeck from "react-bootstrap/CardDeck";
import Card from "react-bootstrap/Card";
import { useEffect, useState } from "react";
import axios from "axios";
import "./App.css";

function App() {
  const [loggedIn, setLoggedIn] = useState(false);
  const [user, setUser] = useState(null);

  useEffect(() => {
    const token = new URLSearchParams(window.location.search).get(
      "access_token"
    );

    axios
      .get("http://localhost:8010/proxy/user", {
        headers: {
          Authorization: "token " + token,
        },
      })
      .then((res) => {
        setUser(res.data);
        setLoggedIn(true);
      })
      .catch((error) => {
        console.log("error " + error);
      });
  }, []);

  return (
    <div className="App text-center container-fluid">
      {!loggedIn ? (
        <>
          <img
            className="mb-4"
            src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
            width="150"
          ></img>
          <h1 className="h3 mb-3 font-weight-normal">Sign in with GitHub</h1>
          <Button
            type="primary"
            className="btn"
            size="lg"
            href="https://github.com/login/oauth/authorize?client_id=8f672e53bc6b92be977d&redirect_uri=http://localhost:8080/oauth/redirect"
          >
            Sign in
          </Button>
        </>
      ) : (
        <>
          <h1>Welcome!</h1>
          <p>
            This is a simple integration between OAuth2 on GitHub with Node.js
          </p>

          <CardDeck>
            {[...Array(3)].map((e, i) => (
              <Card style={{ maxWidth: "25%", margin: "auto" }}>
                <Card.Img variant="top" src={user.avatar_url} />
                <Card.Body>
                  <Card.Title>{user.name}</Card.Title>
                  <Card.Text>{user.bio}</Card.Text>
                  <Button
                    variant="primary"
                    target="_blank"
                    href={user.html_url}
                  >
                    GitHub Profile
                  </Button>
                </Card.Body>
              </Card>
            ))}
          </CardDeck>
        </>
      )}
    </div>
  );
}

export default App;
```

Since we're using the latest version of React (17.0.1), we'll make sure to use only React hooks for the whole implementation.

Let's break it down a bit for a better understanding:

1. First, we make use of the `useState` hook to create two state objects: a Boolean to detect whether the user is logged in and a user object to hold GitHub's response. Both with the respective setter methods.
2. Then, we set up a `useEffect` hook to load only once when the whole component page is loaded and check for the user's details based on the access token provided via query param. If the param is present, let's set both the `user` and `loggedIn` state values so that the logic to render the component will consider them. Otherwise, we simply present the _Sign-up_ component.
3. The `render` function takes care of displaying the proper component based on the current authorization state. We won't go into detail about how React components work here, but feel free to change them with as much of the cool stuff from Bootstrap as you want.
4. Note that we're providing GitHub with the URL for the `client_id` on the button. Make sure to change it with yours.

You may also have noticed that there's a third localhost URL on this code listing: `http://localhost:8010/proxy/user`. The reason is that if we try to access GitHub directly from the UI, we will face some cors-related issues.

The easiest way to deal with this is by creating a local proxy to handle the cors domain conversions for us. The [local-cors-proxy](https://www.npmjs.com/package/local-cors-proxy) NPM module is the perfect choice for this task.

To install it globally, simply run the following command:

```bash
npm install -g local-cors-proxy
```

Then, on the same terminal window, run another command to start it:

```bash
lcp --proxyUrl https://api.github.com/
```

You may see the following output stating that everything went well:

![Starting up the cors proxy server](https://www.honeybadger.io/images/blog/posts/oauth-nodejs-javascript/Figure04.png)
_Starting up the cors proxy server._

It gives you a port at localhost (usually at `8010`) to proxy to that specific URL.

### Adding Some Style

We also need to add some styling before proceeding to the tests. Since we already have an _index.css_ file with some CSS styles, let's switch its content with the following:

```css
html,
body {
  height: 100%;
}

body {
  display: -ms-flexbox;
  display: -webkit-box;
  display: flex;
  -ms-flex-align: center;
  -ms-flex-pack: center;
  -webkit-box-align: center;
  align-items: center;
  -webkit-box-pack: center;
  justify-content: center;
  padding-top: 40px;
  padding-bottom: 40px;
  background-color: #f5f5f5;
}

div#root {
  width: 80%;
}
```

## Testing

To test the client implementation, you may run the following command in the client's root folder:

```bash
npm start
```

Look for errors in the logs and wait until React loads the application on your web browser.

You may see the following screen with the _Sign in_ button. Before clicking it, make sure to have both the server app and the proxy up.

![Sign in with GitHub](https://www.honeybadger.io/images/blog/posts/oauth-nodejs-javascript/Figure05.png)
_Sign in with GitHub._

After clicking the _Sign in_ button, you'll be redirected to the GitHub authorization page, as shown in the image below.

![GitHub authorization page](https://www.honeybadger.io/images/blog/posts/oauth-nodejs-javascript/Figure06.png)
_GitHub authorization page._

In case you're not properly logged in, GitHub will take care of the whole authentication flow by itself. It’s one of the great benefits of this approach; you can delegate user authentication.

Click on the _Authorize_ button, and GitHub will also take care of the redirecting process after it's finished. In the end, you may see the following screen:

![Welcome page with GitHub user information](https://www.honeybadger.io/images/blog/posts/oauth-nodejs-javascript/Figure07.png)
_Welcome page with GitHub user information._

Another test that you can do right after is trying to access the root URI once again and click the _Sign in_ button. You may see that the login process goes smoothly without an authorization page because, once the authorization has been done, its state persists until you revoke them.

## Wrapping Up

You may find the source code for this tutorial [here](https://github.com/diogosouza/oauth2-with-node-js).

It's not uncommon to hear from many beginner devs that OAuth2 is super complicated and too difficult for them to handle. The protocol itself is indeed large; however, you don't have to dig into it all at once.

Many parts of the protocol aren't commonly used. Start simple and then upgrade in small but concise chunks.

There's more to learn about it, such as scopes, authorities, and permissions, 2FA. As the next step, I recommend that you think about storing these access tokens in a database for the server project, as well as in your browser's local storage for the client part. It be great for your users if they could close the browser and come back later to find that the session is still open.