How to Add Protected Routes and Authentication in React

How to Add Protected Routes and Authentication in React

Require authentication for users to access your page
Ferenc Almasi • Last updated 2022 July 05 • Read time 6 min read
Learn how you can add protected routes in your React application and set up user authentication and authorization for your routes.
  • twitter
  • facebook
React

Protected routes are routes that require user authorization in order to be accessed. When you are building a web application, some of your routes may require authentication, which means restricting user access to certain pages or you having your whole application behind a login.

React router is a great way to go when it comes to routing, but you don’t really have the option to protect routes from being accessed by anyone. Luckily the solution to this is really simple and straightforward.

In this tutorial, I would like to show you a simple solution to the problem and how you can too put your React components behind protected routes. We will start from scratch using create-react-app and only include the absolutely necessary things so you can follow along. Without further ado, let’s jump into coding.


Setting Everything Up

After bootstrapping the project with create-react-app, install react-router-dom for routing. We won’t need any other dependencies for this project. With not much modification to the initial create-react-app, this is the current state of the package.json file:

Copied to clipboard!
{
  "name": "auth",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.4.0",
    "@testing-library/user-event": "^7.2.1",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
+   "react-router-dom": "^5.1.2",
    "react-scripts": "3.3.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
package.diff

Adding Routes to the App

We will achieve protection by creating a custom component that will handle incoming requests. We have the plain old Route component in React. This will be used for the sole public route we have, the login page. We also want to have a custom component as well that will handle protected routes. Let’s call it ProtectedRoute.

The purpose of the component will be very simple. If the user has been authenticated and authorized, render the passed component. Otherwise, redirect them to the login page.

Copied to clipboard!
import React from 'react';
import ReactDOM from 'react-dom';
+ import { Route, BrowserRouter, Switch } from 'react-router-dom';
import './index.css';
+ import Login from './Login';
+ import Dashboard from './Dashboard';
+ import Settings from './Settings';
+ import ProtectedRoute from './ProtectedRoute';
import * as serviceWorker from './serviceWorker';

+ ReactDOM.render((
+     <BrowserRouter>
+         <Switch>
+             <Route path="/login" component={Login} />
+             <ProtectedRoute exact={true} path="/" component={Dashboard} />
+             <ProtectedRoute path="/settings" component={Settings} />
+             <ProtectedRoute component={Dashboard} />
+         </Switch>
+     </BrowserRouter>
+ ), document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
index.diff

As you can see, we have imported RouteBrowserRoute and Switch from react-router-dom. I also created some mock components so we can test our router. The ProtectedRoute component will work in the following way:

  • It expects a component prop, the one it should render
  • It also expects a path so it knows what component to render on which URL

Here we defined the Dashboard component two times. This is because we want to land on the Dashboard if no path has been defined. This is what line:15 handles. We also want to load in the Dashboard if the user types in an invalid URL. By omitting the path attribute on line:17, we can tell React Router to fall back to the provided component. So let’s see how the ProtectedRoute component works on the inside.

Looking to improve your skills? Check out our interactive course to master React from start to finish.
Master Reactinfo Remove ads

Creating Protected Routes

So what is inside the custom component we’ve imported? It’s actually really simple, we only have a render function:

Copied to clipboard! Playground
import React from 'react'
import { Redirect } from 'react-router-dom'

class ProtectedRoute extends React.Component {

    render() {
        const Component = this.props.component;
        const isAuthenticated = ???;
       
        return isAuthenticated ? (
            <Component />
        ) : (
            <Redirect to={{ pathname: '/login' }} />
        );
    }
}

export default ProtectedRoute;
ProtectedRoute.js

We get the component from the props and return it if the user has been authenticated. We can also make use of Redirect from react-router-dom. If isAuthenticated turns out to be false, we redirect the user to the login page.

So how do we actually decide if the user is authenticated or not? What should we assign to isAuthenticated?


Authenticating Users

The last step is to authenticate users. This can be done in various ways, your choice of implementation may differ. We can either use cookies, or localStorage, or a combination of both, or maybe something else. Either way, we want to store some information about the user on the client-side so we know when they are logged in.

Copied to clipboard! Playground
render() {
    const Component = this.props.component;
    const isAuthenticated = localStorage.getItem('token');
  
    return isAuthenticated ? (
        <Component />
    ) : (
        <Redirect to={{ pathname: '/login' }} />
    );
}
ProtectedRoute.js

To prevent forgery, this information may be the presence or absence of a token that you also want to verify on your server-side. That way, you make sure that users cannot log in based on only the presence of the token key. It ensures that it is its value that actually matters.

Testing out protected routes in React

As you can see from the gif above, if we refresh the page, it takes us to the login screen. If we try to access any other route that has restrictions, we get redirected back to the login page. If we set a token — no matter the value for now â€” we can access those pages. Also note that if we try to access a route that doesn’t exist while we are logged in, we land on the Dashboard. As soon as we clear the storage, we lose access.


Summary

To summarise: Even though how your protected routes work in React will be obfuscated, the way your client application works can be reverse-engineered. This is why it is essential that everything that involves authentication or authorization should be backed up by a server-side implementation.

Thank you for taking the time to read through, let me know your thoughts on this approach in the comments below. What would be your security solution? đź”’

How to Improve Data Fetching in React With Suspense?
  • twitter
  • facebook
React
Did you find this page helpful?
đź“š More Webtips
Frontend Course Dashboard
Master the Art of Frontend
  • check Access 100+ interactive lessons
  • check Unlimited access to hundreds of tutorials
  • check Prepare for technical interviews
Become a Pro

Courses

Recommended

This site uses cookies We use cookies to understand visitors and create a better experience for you. By clicking on "Accept", you accept its use. To find out more, please see our privacy policy.