Securing a React Web App With Authorization Rules

Divyanshu Maithani
All things #search
Published in
10 min readNov 27, 2017

--

Image: Authorization Roles with a React App

Authentication and Authorization rules are THE MOST IMPORTANT security considerations when building a production app, yet they are often left out as a second thought. In this post, we will be building authentication (Who are you?) and authorization (Are you allowed to see this?) flows to a TodoMVC react app.

The key components that we will be using here are:

  1. ReactiveSearch, a data-driven UI components library for React for building the UI views,
  2. NodeJS/Express, as a middleware server for verifying authentication info and authorization flow,
  3. Auth0, as a hosted authentication service,
  4. Appbase.io, as a hosted database service.
Image: Preview of the final app; tap to try it out live!

You can play with the final app here.

Lets define the two ideas of Authentication and Authorization very clearly before we dive into code.

Concepts

Authentication and authorization are complementary to each other and they solve two different problems:

Is the user who they claim they are? — Authentication

Is this user allowed to perform this action? — Authorization

As you notice in the app preview that the user has to login to modify (or add) todo items, this is the authentication flow in action. Consequently, a user can modify the todos created by them but not the ones created by others. This is authorization.

Before login

In order to implement these features, we’ll make use of access tokens (with Auth0). You can read more on access tokens here.

We’re using Auth0 for handling authentication which uses JWT (JSON Web Tokens) as the access tokens. It comprises three parts header, payload and signature separated by dots(.). A JWT looks like:

xxx.yyy.zzz

The header(xxx) defines the type of token and the algorithm used for hashing. The payload(yyy) contains information about the user and additional metadata. The signature(zzz) is used to verify the sender of the token and ensure the message was not tampered along the way. You can find a more detailed explanation at the JWT introduction guide.

Another popular alternative to using JWT tokens has been managing sessions. However, that introduces statefulness — JWT, being stateless, is a better approach.

The access token once verified tells us that the user is authorized to access the API. Furthermore, it contains additional information on the actions they are allowed to perform (aka scopes or permissions). All of these combine to form the basis of our token based authentication system.

Starting Repository

We’ll be building upon the code from the previous post in this series: Securing a React Web App With Server-side Authentication. You may follow the previous app and use it as a starter project for this article. For convenience, here are the repos:

(i) Client starter repo

(ii) Server starter repo

A few points to keep in mind:

Getting read and write credentials for your app from dashboard
  • We’ll be using read-only credentials on the front-end. Any writes to the back-end (appbase.io) will be done via a Node/Express server.
  • Only an authenticated user is allowed access to add new todos.
  • While adding new todos or modifying existing ones, we’ll check if the user has the permission (again available in the scope) specified with write:todos.
  • If you’ve followed the earlier post in this series, you’ll notice that we’ll restrict modifying the existing todos only by the user who created the todo 🙌.
Final app high level design

The Set Up 🔧

The first step before we get into building the app would be to sign up for appbase.io and auth0.com.

In order to store the todos on appbase.io we would need an app. The simplest way to do it is by cloning the configured example app from this link. Look for the “Clone this app” button on the bottom left corner.

Now that you have setup the appbase.io app, we’ll create a new client in Auth0 for this app. We’ll need the Client ID and Domain in order to configure auth0 in our app. You can get these from your Auth0 dashboard.

Getting the Domain and Client Id

We’ll also need to add a callback URL in the Allowed Callback URLs section since after authentication the client is redirected back to the callback URL with the token info which we’ll later parse for our use. You can add the following to the Allowed Callback URLs:

http://localhost:8001/callback   // our app will run on 8001 port
http://yourhosteddomain:PORT // add if you are hosting it live
Image: Adding Allowed Callback URLs

Next we’ll need to create a new API for the audience identifier (a unique string value used for configuring auth0 instance in the app) which we’ll use later to verify the access tokens.

Image: Getting the audience identifier

Lets also add the scopes we’ll need to this API. We’ll need two scopes (read:todos and write:todos) for our app but you may add as many as you want.

Image: Adding scopes

Building the App

The directory structure of the client will look like this:

Client
├── 404.html // contains redirect script for gh-pages
├── index.html // root index html
├── dist // contains bundle
├── package.json
├── webpack.config.js // webpack configs
└── app
├── auth.js // authentication service
├── Callback.js // used when auth0 redirects to callback URL
├── history.js // used by auth service and react-router
├── index.js // renders the final app and handles routes
├── style.scss // custom styles
├── todoApp.js // parent component for the todo app
├── todoList.js // renders the list of visible todos
├── todoButton.js // button component used in the footer
├── todoFooter.js // child component which renders footer
├── todoItem.js // child component which renders todo item
├── todoModel.js // logic for handling requests
├── todomvc.scss // todomvc core styles
└── utils.js // utility methods

The directory structure of the express server will look like this:

Server
├── index.js // handles routes and requests
└── package.json

Here are the final repositories so you can refer to them anytime:

(i) TodoMVC Authorization Client

(ii) TodoMVC Authorization Server

1. Getting the projects ready

We are starting with the TodoMVC code from this previous post and adding an authorization flow to it. Lets start by cloning these repositories into your local environment:

(i) Client starter project

(ii) Server starter project

After you’ve cloned the projects you can switch into the server project directory and fire it up:

npm install
npm start

In a different terminal tab/window do the same for client project:

npm install
npm start

The server should start by default on port 8000 and the client app on 8001. Test out the starter app in your browser at http://localhost:8001.

2. Updating Authentication Service

Open the client project in your favorite text-editor and start by updating the app/history.js file if needed. You might need to update the basename value to your github repo name:

Updating the history service

Next, lets update the app/auth.js file. The first thing you need to update here are your Auth0 clientID, redirectUri, audience and domain. We’ll also add another key here called scope with value as this.requestedScopes.

requestedScopes = 'openid profile email read:todos write:todos';

Adding a scope key-value to the object passed to auth0.WebAuth function will cause the returned access token from Auth0 to have a scope key in the payload. We can use this information to correctly determine the scopes of a user:

(i) Check the returned token for scopes. If it’s populated, it means that a different set of scopes were allocated than the requestedScopes. In this case we’ll treat the returned scopes as final.

(ii) If the returned token has no scopes, it means all the requestedScopes were granted and we’ll treat them as final. Thus the logic can be summarized as:

scopes = authResult.scope || this.requestedScopes || '';

Note

We’re allocating the same set of scopes to all users for simplicity. This can also be done conditionally using Auth0 rules. For example, you can use these rules to allow or reject a set of scopes requested by a user.

We’ll also add a couple of methods, userHasScopes to check for the scopes available to the user and getUserEmail to return the user’s email. Check out the updated annotated file below:

3. Updating TodoModel

app/todoModel.js powers all the operations that we can do on the todo items. First thing to update here is your server URL. We’ll be later deploying the server on now (which you may update later) but for now you can update the development server URL if you wish to run the server at some other port:

const server = process.env.NODE_ENV === 'development' ? 'http://localhost:8000/' : 'https://todomvc.appbase.io/authorization/';

Next, you can update the app and credentials passed to the Appbase constructor. These are the same ones that you got after cloning the app from your appbase.io dashboard. You should user your read-only credentials here since client side code is never fully secure. We’ll be using the write-level credentials on the server to create, update or delete the todo items.

We’ll introduce a couple of methods to the TodoModel class at app/todoModel.js to filter out the todos created by the user and those created by others. Also we’ll modify the toggleAll and clearCompleted methods to use these new functions for updating only the relevant data (created by the user).

Updating todo model

4. Updating UI elements to check for scopes

First lets update app/todoApp.js to check for write:todos scope before allowing user to perform write operations. We can use the userHasScopes method from the authentication service in app/auth.js to handle this. Also update the todos in render function to only take todos for current user into account for the toggle button. Also update the app and credentials here in ReactiveBase component.

Updating TodoApp

Next, we’ll add similar checks to app/todoList.js to check for write:todos scope before performing an operation. Additionally, we’ll also verify that the todo item to be modified was created by the user. We can also render a message at the end conditionally if the user has no write:todos permission in the scope and a message if they try to modify the todos not created by them.

We’ll add a couple of methods showWarning and hideWarning to update the showWarning flag in the component state and conditionally render the message.

Updating TodoList

This wraps up the client side code. Lets move on to the server side code 😁.

5. Adding authorization checks to the server

We’ll be adding express-jwt-authz and node-fetch into the project. If you’re following from the starter project for the server, simply add them using npm or yarn.

yarn add express-jwt-auth node-fetch

You’ll also have to use your own Auth0 configurations here, for example jwksUri, audience, issuer. We’ll add a method to check for write:todos scope in the access token sent by the client.

We’ll create a method to fetch the user information from the /userinfo endpoint of your auth0 domain. Check here for detailed information. We’ll use this information to get the user details like nickname, picture, email.

In the Appbase constructor parameter, pass your appbase.ioapp and credentials. Note that you should pass write-level credentials here since we wish to perform write operations on the data.

Next, we’ll create a function verifyCreatedBy which checks if the todo to be modified was created by the user who sent the request. We will do it by returning a promise which will only resolve if the user is the creator of the todo item.

Note

We already performed a check on the front-end to allow modifications on the same condition. However, client side code is never secure. Therefore, even if someone manages to manipulate your client side code to perform an unauthorized action, the server would reject the request.

Final server code

6. Deployments

The example client app is deployed on github pages. The starter project includes all the necessary configurations and can be deployed easily by creating and pushing to a gh-pages branch.

The server can be deployed easily using now. The starter project includes a now-start in package.json which is used by now to start the project. If you haven’t installed now you can do so by running:

npm i -g now

After it’s installed you can switch into your server directory and run now.

After following through the instructions, you can use the URL provided by now in you client app. Your TodoMVC authorization app is ready to roll! 🚀

tl;dr

In this post, we built authentication and authorization flow for a realtime TodoMVC example app and added a node server which allows only authorized users to modify the data. We used auth0 to handle authentication and appbase.io as our database for storing all todo items. You might find the following links useful:

  1. TodoMVC Authorization Client starter and final repo.
  2. TodoMVC Authorization Server starter and final repo.
  3. Final app preview.
  4. ReactiveSearch, A React UI library for building data driven components, for the client app.
  5. Appbase.io, The Streaming NoSQL Database, as our todos database.

Further readings

If you liked this article, you’ll definitely enjoy the previous posts in this series 😉

Learn how to build reactive apps — one post at a time!

--

--