How to do secure Client-Side React apps with protected routes using Apps’ memory!?

Max Shahdoost
4 min readJul 1, 2022
Ashwini Chaudhary (Unsplash)

TL;DR
If you want to get straight to the implementations just skip the introduction in the beginning and jump to the solution section.

We are using Node.js back-end and React.js Front-End, cookies, React-Router-Dom V6 and Axios.

Introduction:

If you are an experienced Front-End developer or even a Junior developer, you should have already struggled with some web applications with many protected routes that users must log in to have access to the app and these kinds of apps are very common these days especially dashboards with strong authentication behind them.

Since I’ve struggled a lot on how to secure a CSR (Client-Side-Rendering) React.js Application and not using LocalStorage or Cookies to store highly sensitive information like Access Tokens, user’s data, or anything related to them because of the less security LS or Cookies have while they can be attacked by an outsider using a javascript code from outside sources like most famous ones XSS (Cross-Site-Scripting) or CSRF (Cross-Site-Request-Forgery), It is strongly recommended to use your Javascripts’ memory (eg: Redux) to internally store these highly sensitive data to prevent such hacking techniques to access to that information but there is a big downside with doing so.

Problem:

If you store all the information related to the user’s authentication in your web apps’ memory, then on each hard-refresh of the Browser, change tabs, go back and forward with browser-controlled buttons, etc, they will all be wiped out and there is no Access Token or any other data available to you so your users must log in into your app again and this loop will go on and on.

This will heavily affect your user's experience throughout their usage and they won’t be happy with this!

Solution:

After much research, I’ve done to find a proper solution for these I’ve come up with the idea of still storing all the sensitive data in the apps’ memory but finding a way around the exceptions like hard-refresh or any other action that can result in deletion of apps’ state.

Refresh Tokens is the way to go!

Secure a Client-Side-Rendering App using Refresh Tokens and Cookies

I’ve created the above architecture for a sample Client-Side-Rendering React.js Application which is having a central store like Redux.

As you can see, we have a server here that every time we log in we do an HTTP request to our server app to validate and get back all those sensitive data like access-token and user’s data.

If we request for login, we will get back our token and refresh-token, the important part is here: the server must set an HTTPonly and Secure cookie to our browser containing the refresh-token data in it.

On the other hand, once we logged in, we store all the data such as token and user’s info inside our internal app state and now we have a secure and HTTPonly cookie in our cookies.

We need to create a wrapper for all our protected routes on the top level of our app when we define our routes with this wrapper component which requests for refresh token when our app has no authentication in its memory which will happen only once when the app is mounted on the DOM.

First of all, we create our useRefreshToken hook like this:

useRefreshToken

Then we will create our wrapper which I mentioned previously to watch for the authentication:

Persist Wrapper component for All Protected Routes

And finally, that’s it, you can now persist your user’s data and access token inside your apps’ memory without bothering your users to log back in each time the app’s memory is wiped away and you also secured your highly sensitive data from hackers that may want to damage your application!

Note1: Don’t forget that your server should give you back a new Access Token and all the important data you need for your app with the refresh token flow same as it does in login flow.

Note2: If you are having a very high sensitive app like a Bank web app or something similar that needs yet another level of security, you may want to still go with the memory but force people to log in after any unusual behavior such as hard refresh or back and forward button clicks because these use cases need another level of security and you don’t want to risk a new login/logout for a better user experience in Banking applications!

--

--

Max Shahdoost

A highly passionate and motivated Front-End Software Engineer with a good taste for re-usability, security and developer experience.