Securing a React app with a basic server-side authentication
Tags: react
Published: 2019-07-08 20:23:43

---
title: Securing a React app with a basic server-side authentication
published: true
tags: react
canonical_url: https://blog.logrocket.com/securing-a-react-app/
---

![](https://thepracticaldev.s3.amazonaws.com/i/k109oi5onzwbpi79xqr1.png)

It is well known that the client side is unsafe due to its exposed nature. In your web application, you can conditionally render views to show different contents to different users, but if that information is already stored in the client side, it is no longer secure.

In order to make sure that only the users with credentials can see the limited content, you should ship the content data from your server upon authentication.

This article will walk you through how to do that through basic authentication for an [Express server](https://expressjs.com). Although the client side will be a React app, you can virtually apply it to any other client-side applications.

## Basics

In this article, I assume that you already know how to create and build a React project from scratch, so I will mostly focus on the server-side implementation.

The easiest way to bootstrap a React project is obviously using `create-react-app` package. When you create a project with this package and then run `npm start` you basically start a Webpack server. This works fine on your local machine, but when you want to deploy it to a remote server, you need your own server to serve your React application, which is basically a package of HTML, JavaScript, and CSS.

I will be referring to the following folder structure for this example project:

```jsx
--- Project Folder
 |__ client (React App)
 |__ server.js
 |__ package.json
```

So, there is a `Project Folder` and inside it, we have a `client` folder containing the React App and also a `server.js` and `package.json` files, which you can create by using the following commands on terminal in the project directory.

```jsx
npm init -y
touch server.js
```

## Serving the React app

### How to proxy the React app

Your deployed React application will be built and the `build` folder will be served from an Express server. However, when developing your React App locally, you shouldn’t be building for production on every single change. In order to avoid this, you can proxy your React app to a certain port and thus you would be using the built-in Webpack server for running the React app locally and could still communicate with your Express server.

In order to do that you should add the following line to `project.json` file of your React app, assuming that Express server will be serving on port 5000.

```jsx
“proxy”: “http://localhost:5000/"
```

### Serve the build folder

The express server should be serving the build folder, which will be created during the deployment to a remote server.

The following snippet is a basic Express server. We will be adding authentication and other things on top of it.

```jsx
const express = require('express');
const path = require('path');
const app = express();

const PORT = process.env.PORT || 5000;

app
  .use(express.static(path.join(__dirname, '/client/build')))
  .listen(PORT, () => console.log(`Listening on ${PORT}`));

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '/client/build/index.html'));
});
```

### Run it locally

As mentioned earlier, the React app will still be using the Webpack server as it will proxy to port 5000. However, we still have to run the Express server separately.

`Nodemon`package is very handy for running and listening for changes, so you can install it globally and then run the server by simply running the following command in the main directory of the project folder.

```jsx
nodemon server.js
```

As for the React app we only have to run the following command inside the **client** folder.

```jsx
npm start
```

### How to run on a remote server

Although this is an optional step, it’s important to mention. Let’s assume we want to deploy our application to a [Heroku](https://www.heroku.com) dyno.

Heroku detects a NodeJS application and installs dependencies and runs it automatically, but you still have to tell it to go into the specific folder, install dependencies and build the React app for production, which is going into `/client` running `npm install` and then `npm run build` respectively in our case.

For this purpose, Heroku has a post-build command:

```jsx
"heroku-postbuild": "cd client && npm install && npm run build"
```

Add this under `"scripts"` key inside the `package.json` of the server.

Also, make sure that your entry point for the NodeJS application is `server.js` in the `package.json` file. This is likely to be `index.js` if you initialized your npm package with `-y` flag as `npm init -y`.

```jsx
"main": "server.js"
```

## Basic authentication

As the name suggests `express-basic-auth` is a very convenient and easy to use package for basic authentication purposes.

Install the package and then require it at the top of your `server.js`. Then we define the credentials by using the instance of the package.

```jsx
const basicAuth = require('express-basic-auth');

const auth = basicAuth({
  users: {
    admin: '123',
    user: '456',
  },
});
```

Now when the `auth` variable is used as a parameter of an end-point, response from this end-point reaches back to the client if, and only if, the credentials sent along with the request match.

In the code shown below see both `/authenticate` end-point on the server side and the `GET` request sent from the client along with the `auth` object, which contains the credentials.

```jsx
// End-point on Server

app.get('/authenticate', auth, (req, res) => {
  if (req.auth.user === 'admin') {
    res.send('admin');
  } else if (req.auth.user === 'user') {
    res.send('user');
  }
});

// Request on Client

const auth = async () => {
  try {
    const res = await axios.get('/authenticate', { auth: { username: 'admin', password: '123' } });
    console.log(res.data);
  } catch (e) {
    console.log(e);
  }
};
```

Looking at the example above, passing the correct credentials sends back either `admin` or `user` as a string response depending on the username used. Wrong credentials simply return a response of `401 (Unauthorized)`.

So far we figured out how to send data from server to client if the credentials are correct. So, now the next step would be persisting that authentication through a [cookie session](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies).

Instead of sending a response from `authenticate` end-point, we can set a cookie on the client from server. By deploying another end-point we then can check for the cookie and actually send the data to populate the view.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

### Cookie-session

Once the user is authenticated, this information should be stored somewhere on the client side so that the user does not authenticate every time. The common practice is to use cookies to store this session information. Cookies are safe as long as the correct flags are set.

**httpOnly:** This flag ensures that no client-side script can access the cookie, but the server.

**secure:** This flag ensures that cookie information is sent to the server with an encrypted request over the HTTPS protocol.

When using `secure` flag, you also need a key to sign the cookie. For this purpose, we use `cookie-parser` middleware for Express server.

A cookie simply has a **name** and a **value**. Even with the aforementioned flags, never disclose any vulnerable information within cookie parameters.

In the code shown below, you can see the `server.js` which sets a unique cookie upon authentication.

As you can see after setting the cookie, the response is also sending an object with `screen:admin` or `screen:user` key/value pair.

This response will later be utilized in the React application on the client side.

```jsx
const cookieParser = require('cookie-parser');

// A random key for signing the cookie
app.use(cookieParser('82e4e438a0705fabf61f9854e3b575af'));

app.get('/authenticate', auth, (req, res) => {
  const options = {
    httpOnly: true,
    signed: true,
  };

  if (req.auth.user === 'admin') {
    res.cookie('name', 'admin', options).send({ screen: 'admin' });
  } else if (req.auth.user === 'user') {
    res.cookie('name', 'user', options).send({ screen: 'user' });
  }
});
```

Since the cookie has a `httpOnly` flag, we can neither read nor delete it on the client side. Therefore, we need two more end-points to read and delete the cookie and send back a response accordingly.

### How to read/delete a cookie from the server

Reading and deleting a cookie from a server is quite straightforward, but you should keep in mind that the end-points for these functionalities should not have the `auth` variable, since authentication for these end-points should not be required.

So below we have two endpoints; `/read-cookie` and `/clear-cookie`.

The `signedCookies` object with the `res` contains the `name:value` pair that we set for the cookie.

```jsx
res.cookie(‘name’, ‘admin’, options)
```

So, depending on the value of the cookie name, we send a response.

As for the `/clear-cookie` end-point, deleting the cookie is simply done by referring to the name of the cookie, which is `name`.

```jsx
app.get('/read-cookie', (req, res) => {
  if (req.signedCookies.name === 'admin') {
    res.send({ screen: 'admin' });
  } else if (req.signedCookies.name === 'user') {
    res.send({ screen: 'user' });
  } else {
    res.send({ screen: 'auth' });
  }
});

app.get('/clear-cookie', (req, res) => {
  res.clearCookie('name').end();
});
```

By following this logic, you can create several different end-points to send different types of data depending on your application. All you need to do is check the cookie and send the response accordingly.

Below you can find the complete `server.js` file, which serves the client side React application that will be covered in the next section.

```jsx
const express = require('express');
const basicAuth = require('express-basic-auth');
const cookieParser = require('cookie-parser');
const path = require('path');

const app = express();

const auth = basicAuth({
  users: {
    admin: '123',
    user: '456',
  },
});

const PORT = process.env.PORT || 5000;

app.use(cookieParser('82e4e438a0705fabf61f9854e3b575af'));

app
  .use(express.static(path.join(__dirname, '/client/build')))
  .listen(PORT, () => console.log(`Listening on ${PORT}`));

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '/client/build/index.html'));
});

app.get('/authenticate', auth, (req, res) => {
  const options = {
    httpOnly: true,
    signed: true,
  };

  console.log(req.auth.user);

  if (req.auth.user === 'admin') {
    res.cookie('name', 'admin', options).send({ screen: 'admin' });
  } else if (req.auth.user === 'user') {
    res.cookie('name', 'user', options).send({ screen: 'user' });
  }
});

app.get('/read-cookie', (req, res) => {
  console.log(req.signedCookies);
  if (req.signedCookies.name === 'admin') {
    res.send({ screen: 'admin' });
  } else if (req.signedCookies.name === 'user') {
    res.send({ screen: 'user' });
  } else {
    res.send({ screen: 'auth' });
  }
});

app.get('/clear-cookie', (req, res) => {
  res.clearCookie('name').end();
});

app.get('/get-data', (req, res) => {
  if (req.signedCookies.name === 'admin') {
    res.send('This is admin panel');
  } else if (req.signedCookies.name === 'user') {
    res.send('This is user data');
  } else {
    res.end();
  }
});
```

## A practical example with a React app

Assume you have an **admin** screen and a regular **user** screen, which you show different contents on.

- The first thing we need is the authentication request, which we sent the credentials to the server.
- We need another request that we send from `componentDidMount` life-cycle hook to check if there is already a cookie so that we can log in automatically.
- Then we might need some other requests for getting extra data.
- Eventually, we need to be able to send a request to clear the cookie so that the session does not persist anymore.

Below you can find the complete client-side code. However, in order to get it working, obviously, you should run it alongside the server.
{% codesandbox o73bh %}
Let’s go through the important steps of the React app.

We have three different state variables; `screen`, `username`, `password`.

As the name suggests `username` and `password` is for storing the input field data and sending it to the server over `/authenticate` end-point through `auth` function. Therefore the `onClick` event of the login button calls the `auth` function. This is only required if the user is authenticating initially.

In order to check if the user already logged-in, there is `/read-cookie` end-point used in `readCookie` function. This function is called only once on component mount. The response from this end-point sets the `screen` state to change the view to `admin` screen or `user` screen.

In this example, both `admin` and `user` screens are the same component, but since the response from the server changes depending on the authentication, the same component renders different contents.

Additionally, `/get-data` end-point demonstrates another example for the use of cookie specific response from the server.

Lastly, `/clear-cookie` is used with `onClick` event of the logout button to clear the cookie and set the `screen` state variable back to its initial state.

## Conclusion

By reading through this article, you get the idea of basic server-side authentication on an Express server with `express-basic-auth` npm package. The use case of such a simple authentication system can be any type of small size personal projects or a secured page for an interface with a fixed number of users, you name it.

* * *

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
[![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?zoom=2&resize=730%2C412&ssl=1)](https://logrocket.com/signup/)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).

* * *

The post [Securing a React app with a basic server-side authentication](https://blog.logrocket.com/securing-a-react-app/) appeared first on [LogRocket Blog](https://blog.logrocket.com).