Thinking of implementing authentication to your fullstack app? Start here
Tags: webdev, beginners, react, security
Published: 2021-12-10 19:03:03

> Don't roll your own crypto.

It is incredibly difficult to be truly "secure" with your own solution, not to mention after you have publicized your application to the web, opening up your vulnerability to the world. Thus creating an unpleasant experience for your users.

So it is best to utilize PhD+++ level libraries to make sure these are up to code with the best of their capabilities. [But even then I would not feel so invincible.](https://xkcd.com/538/)

The principle is sound, if you don't roll your own crypto, why should you do the same for authentication?

## The DIWhy Way

Imagine you're designing a responsive, ultra-fast web app. You already figured out what Javascript framework to use, which component library to design the frontend with and how the backend would serve the frontend. Now you need an auth system to keep track of users and do more user-specific things. So you came up with this checklist:

### The checklist

- New User Table in your Database
- Frontend login component
- Register new users component
- Making sure the user is authenticated until some timeout or logout throughout the site traversal.
- How to handle log out?
- Auth injectors to check and secure certain endpoints in the app.
- ...

After a while, you start to feel overwhelmed and decide to get on this tomorrow instead.
Clearly, this is a lot of work for something that you want as a nice add-on. It's kind of like playing with the Russian Doll, the more you open it up the deeper it goes.

![Russian dolls](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lrd5cyfd2kpuf5ka6bw.jpg)

And then you run into conundrums to determine which kind of authentication flow works best and how the heck would you implement it. What about speaking to the backend? Or when you want to secure your API endpoint as well, it keeps going.

Auth0 aims to mitigate all these issues for you.

## The Auth0 Way
**Firstly, this is not an Ad for Auth0, and I'm simply repurposing and comprehensively compiling information to serve my specific use case. All rights are reserved with Auth0 regarding the blog posts and resources.**

Taken straight from their main website:

> Auth0 is easy to implement, adaptable authentication and authorization platform.
>
> > Basically, we make your login box awesome.

In a nutshell, Auth0 is a 3rd party solution that takes care of 80% of [the checklist](#the-checklist), and some, on its server.

So how easy is it really?

## Implementing Auth0

Let's assume our application is a backend Flask running the API and serving the React frontend (Single Page Application) bundled by Webpack.

So you might start by asking, "why don't I just let the backend handle the auth, propagating credentials and handling all authentication checks for both frontend and backend?". For one, it makes the communication between the two more complicated, as well as not creating a seamless workflow for your users.

A specific example would be that: The frontend directs the user to be authenticated, and since the backend handles communications with Auth0, the login/logout endpoints/views must be served directly by the API. This already is a bad idea since we should separate the view (to Frontend) and the controllers (to Backend). Another big issue is that the authentication flow requires multiple redirections, by which the frontend (who is waiting for the backend to return a valid access_token) will lose its connection.

We mitigate this by actually letting the Frontend(client) deal with asking for access tokens. Which we can use towards authenticating for any API requests to the backend. This workflow is called `Implicit Flow`.

![Implicit Flow](https://images.ctfassets.net/cdy7uua7fh8z/2qt25H8agpipvMYQngZu0X/1870c664d33eb127d07953e2b9cf5321/implicit-grant.png)

## Auth0 Configuration

Go ahead and [create an account with Auth0](https://auth0.com/signup). And login.

Since Auth0 manages all of our users' credentials and scopes, we can go ahead and create an `API`, this will help us define scopes like `data:read`, `data:write`,... whatever. These scopes are all custom and they can be whatever we want, essentially labels for our business logic.

### API

Navigate to [API section](https://manage.auth0.com/#/apis) of the Dashboard then we can **Create API**

![Create API](https://images.ctfassets.net/cdy7uua7fh8z/4kIH4BQc4vDel2zhJ6lwCE/6812a7ec36119cde0fbe10801e4c0394/dashboard-apis-create_create-popup_default-empty.png)

A couple notes:

- `Name`: can be anything
- `Identifier`: This should just be something like `https://<BASE_URL>/api` (example: `https://127.0.0.1:5000/api`). This can **not** be changed later.

After its creation, we can go to the `Permissions` tab in the API and create some scopes/permissions for our users. You can add `data:read` for simplicity, this can be finetuned to as granular as you like.

### Single Page Application

Then we create a Single Page Application Type at [Applications Page](https://manage.auth0.com/#/applications) by `+ Create Application` and choosing `Single Page Web Applications`. You can put whatever name you like.

![Create Application](https://images.ctfassets.net/cdy7uua7fh8z/Txo1qMBEJEjsi7p0q3NqS/4778c79b7779cb00344dadb02e61f208/dashboard-applications-create_create-popup-spa_default.png)

And we are done for the time being.

### Machine-to-Machine application (optional)

This part is completely optional, but it allows the user to request an access token through a [CURL request](https://hackernoon.com/how-to-easily-use-curl-for-http-requests-db3249c5d4e6). Which they can use to call protected endpoints of the Flask application.

All we gotta do is create a `Machine to Machine App` through the [Applications Page](https://manage.auth0.com/#/applications) by `+ Create Application`.

No extra configuration is required.

## Frontend

Implementing Auth0 to React is as easy as it gets, for which Auth0 has a great quick start guide [here](https://auth0.com/docs/quickstart/spa/react/01-login).

**NOTE**: During the URL configuration in the Auth0 Application settings:

- Auth0 only accepts **HTTPS** connections so you would need to either proxy your local instance to self-sign to call Auth0 endpoints. For Flask, you can simply add `ssl_context="adhoc"` to the `run()` method of the Flask backend:

```python
app.run(ssl_context="adhoc")
```

- Auth0 backend does not accept `localhost` in Application URIs, for which you can simply use `https://127.0.0.1:<PORT>` (example: `https://127.0.0.1:5000`)
- Additionally, make sure `Refresh Token Rotation`, `Refresh Token Expiration (Absolute and Inactivity)` are enabled. Which will allow us to persist tokens with the browser `localstorage`. These settings can be located below `Application URIs`

While optional, I recommend to use this wrapper for the `Auth0Provider` component:

```jsx
import React from "react";
import { useNavigate } from "react-router-dom";
import { Auth0Provider } from "@auth0/auth0-react";

const Auth0ProviderWithHistory = ({ children }) => {
  const navigate = useNavigate();

  const onRedirectCallback = (appState) => {
    navigate(appState?.returnTo || window.location.pathname);
  };

  return (
    <Auth0Provider
      domain="YOUR_DOMAIN"
      clientId="YOUR_DOMAIN"
      redirectUri={window.location.origin}
      scope="read:data"
      useRefreshTokens
      cacheLocation="localstorage"
      onRedirectCallback={onRedirectCallback}
    >
      {children}
    </Auth0Provider>
  );
};

export default Auth0ProviderWithHistory;
```

**EXPLANATION**:

- `domain`, `clientId`, `redirectUri` are defined like from the [quickstart](https://auth0.com/docs/quickstart/spa/react/01-login).
- `scope` requests that this user be granted the permission that we defined in the [API](#api)
- `useRefreshTokens` tells Auth0 to also return refresh tokens along with the access token. [Learn more](https://auth0.com/docs/security/tokens/refresh-tokens)
- `cacheLocation` using `localstorage` for this means that the tokens are saved locally on the browser. This is secure since we enabled `Refresh Token Rotation`, `Refresh Token Expiration (Absolute and Inactivity)`. [Auth0 explains why very well](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/#Keeping-Refresh-Tokens-Secure)
- `onRedirectCallback` essentially returns the user to the page where they were redirected to login.

**NOTE**: If you are using any Component library which requires the Provider to wrap the `<App>`, make sure Auth0 Provider is a child of that element.

Example: Using with [Chakra UI](https://chakra-ui.com/) with [React Router V6](https://reactrouter.com/). `ColorModeScript` is a Chakra UI thing where you can set default colour schemes.

```jsx
ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <ChakraProvider>
        <Auth0ProviderWithHistory>
          <ColorModeScript initialColorMode={theme.config.initialColorMode} />
          <App />
        </Auth0ProviderWithHistory>
      </ChakraProvider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById("root")
);
```

Then to protect your React endpoints, simply return your pages/components wrapped with `withAuthenticationRequired`
Example: Loading component is simply a looping gif to prevent pages flashing.

```jsx
import React from "react";
import { withAuthenticationRequired } from "@auth0/auth0-react";

import Loading from "../components/Loading";

function Page() {
  return (
    <div>
      <h1>New Page</h1>
    </div>
  );
}

export default withAuthenticationRequired(Page, {
  onRedirecting: () => <Loading />,
});
```

---

After configuring endpoints and authentications, we now would like to call our (to-be) secured backend, simply retrieve the access token and add it as a `Bearer` token as part of the HTTP request. As also documented [here by Auth0 in the quickstart](https://auth0.com/docs/quickstart/spa/react/02-calling-an-api).

## Backend

As mentioned we would be authenticated through the access token, which is a [JWT](https://jwt.io/), and the backend would need to understand the signing algorithm to decipher it. The signing algorithm can be found with the [API](#api) that we created in Auth0.

Below is the code that handles errors, checking the validity of token and scope,...

{% gist https://gist.github.com/MaXeraph/00c570040044ee0c6c58603fe8cb69db %}

This snippet was taken from my own project by which I used [Blueprint](https://flask.palletsprojects.com/en/2.0.x/blueprints/) to modularize services and reduce circular imports.

Here we defined multiple decorators to attach to endpoints with examples of how to handle each auth case:

```python
# This doesn't need authentication
@bp.route("/test/public")
@cross_origin(headers=["Content-Type", "Authorization"])
def public():
    response = (
        "Hello from a public endpoint! You don't need to be authenticated to see this."
    )
    return jsonify(message=response)


# This needs authentication
@bp.route("/test/private")
@cross_origin(headers=["Content-Type", "Authorization"])
@requires_auth
def private():
    response = (
        "Hello from a private endpoint! You need to be authenticated to see this."
    )
    return jsonify(message=response)


# This needs authorization
@bp.route("/test/private-scoped")
@cross_origin(headers=["Content-Type", "Authorization"])
@requires_auth
def private_scoped():
    if requires_scope("read:data"):
        response = "Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this."
        return {"message": response}
    raise AuthError(
        {
            "code": "Unauthorized",
            "description": "You don't have access to this resource",
        },
        403,
    )
```

And it's as simple as that, `cross-origin` to enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) on a per endpoint basis; `requires_auth` to extract `Bearer` tokens and validate it with Auth0.

## Conclusion

I hope this gives you a nice and quick way to implement the authentication step so you can focus more on the business logic of the application.

Any question? Leave a comment below and I'll do my best to help out!

## What is...?

- An Access Token: obtained by authenticating the user with an Authorization Server and the user can then, in turn, authorize the application to access the API on their behalf.
- JWT: Credentials, which can grant access to resources