DEV Community

Cover image for Implementing Protected Route and Authentication in React-JS
OlumideSamuel
OlumideSamuel

Posted on • Updated on

Implementing Protected Route and Authentication in React-JS

Almost every web application require some form of authentication to prevent unauthorized users from having access to the inner workings of the applications.

For this tutorial, I'll be showing how to set up an authentication route and protect other routes from been accessed by unauthorized users.

First things first,

Install all dependencies

  • npm i react-router react-router-dom
  • Add link to bootstrap CDN in ./public/index.html header

React-router will handle our routing, i.e switching from one page to another within the web application.

Note: We'll be building ontop of the last project from Structuring redux in a web app

Home Component

First, in ./src/view/Home let's create an Home component which will serve as our homepage. Note: onComponentDidMount, some demo data are fetched from https://jsonplaceholder.typicode.com/users to populate the landing page.
Homepage

Signin Component

In ./src/view/Authentication/Signin.js, let's create the Signin component,
Signin Component

OnInputChange, update the userData state with the current value;

 const handleInputChange = (e) => {
    setUserData((prevState) => {
      return {
        ...prevState,
        [e.target.name]: e.target.value,
      };
    });
  };
Enter fullscreen mode Exit fullscreen mode

OnSubmit, if username and password supplied by user matches admin and 123456 respectively,

  • save isAuthenticated as true in localStorage,
  • else display appropriate error message. The data saved in localStorage will be used later to confirm Authentication status.
const handleSubmit = (e) => {
    e.preventDefault();

    //if username or password field is empty, return error message
    if (userData.username === "" || userData.password === "") {
      setErrorMessage((prevState) => ({
        value: "Empty username/password field",
      }));

    } else if (
      userData.username.toLowerCase() === "admin" &&
      userData.password === "123456"
    ) {
      //Signin Success
      localStorage.setItem("isAuthenticated", "true");
      window.location.pathname = "/";
    } else {
      //If credentials entered is invalid
      setErrorMessage((prevState) => ({ value: "Invalid username/password" }));
      return;
    }
  };
Enter fullscreen mode Exit fullscreen mode

App.js Component

In ./src/App.js, add the created component to the BrowserRouter from react-router-dom,

App.js

...

At this point, if the project is launched, we'll be redirected to the landing page because the path="/" points to the Home component. However, it'll be great to protect the route such that only authenticated users can have access to that route and every other user redirected to the Signin Page. This leads us to the concept of Protected Route in React-JS.

Protected Route

Protected Routes are routes that can only be accessed if a condition is met(usually, if user is properly authenticated). It returns a Route that either renders a component or redirects a user to another route based on a set condition.

In ./src/components/ProtectedRoute.js,

  • create a functional component that accepts component and other route details as props, and then
  • check a condition to confirm if user is authenticated or not. (In our case, we'll be getting isAutheticated from localStorage)
  • if the value is true, render the component, else, Redirect route to /signin page.
import React from "react";
import { Redirect, Route } from "react-router-dom";

function ProtectedRoute({ component: Component, ...restOfProps }) {
  const isAuthenticated = localStorage.getItem("isAuthenticated");
  console.log("this", isAuthenticated);

  return (
    <Route
      {...restOfProps}
      render={(props) =>
        isAuthenticated ? <Component {...props} /> : <Redirect to="/signin" />
      }
    />
  );
}

export default ProtectedRoute;

Enter fullscreen mode Exit fullscreen mode

In ./src/App.js, import the ProtectedRoute and pass the Home component into it,
Protected route

Let's add logout button to the Homepage, onClick the logout button, clear the isAuthenticated value in localStorage, and then redirect the route to Sign-in page.

Alt Text

Test run the Application

When the application is launched, it'll redirect to the Sign-in page,
signin

To test if the protected route work as it should, edit the route to visit the homepage as http://localhost:3001/ , you'll notice the app will be redirected back to the Signin page at http://localhost:3001/signin page.

Landing Page
After a successful login with the set credentials: Username: admin, Password: 123456,

landing page

...
Here is a link to the code on github.
I hope this is helpful to someone. Like, share and bookmark. :)
...

If you would be interested in implementing an automatic logout feature after some seconds of user inactivity, read this next article I wrote - Implementing AutoLogout Feature in Web Applications (React)

Top comments (24)

Collapse
 
bapin93 profile image
Andres Pineda

Question, what keeps an attacker from going to my app, opening dev tools and creating "isAuthenticated" and setting it to true in localStorage? Wouldn't that person then be able to access the protected route without actually authenticating? Obviously they would need to know that the code looks for this particular key in local storage, but it does seem like a security vulnerability. I currently have this problem with my app and want to see if anyone has ideas for a solution!

Collapse
 
orifl1221 profile image
OriFl1221

No. Usually the reason protected routes are protected is because these routes communicate with the backend server in a way that requires authentication, and simply changing "isAuthenticated" wont make the backend server trust us, since our credentials that we pass in the requests are the same.

Collapse
 
bapin93 profile image
Andres Pineda

Thanks for the reply. I understand that an API call will have it's own authentication (Authorization Header Bearer token or similar). I'm not worried about that part. Say I have a static page (no server communication) that's behind a protected route using the method in this article. If I set "isAuthenticated" to true in the dev tools, I am able to access the content of that page, even without actually authenticating.

Thread Thread
 
nipunamarasena profile image
NipunAmarasena • Edited

I faced the same issue. Setting a key in localstorage with localStorage.setItem("isAuthenticated", "true") allowed access to the protected route. However I added a new state variable to redux and updated it to be true in when loging API returns.

After that when checking for auth in protected route, i checked both localStorage keyvalue and the newly created state variable (which only gets TRUE when the login API call succeeeds)

Thread Thread
 
bapin93 profile image
Andres Pineda

Interesting approach. What happens to the Redux state variable when the app is refreshed in the browser? Wouldn't you lose that state and the user would have to reauthenticate?

Thread Thread
 
slex1onemusdy profile image
Mustafa

You can use refresh tokens for this problem. You can create a router on the backend that generates new access tokens with the current token you have. Then on the front end, you can create a function with setTimeout, and basically, you can get a token in every specific second until the user logs out of your system. Thus you can keep the user's session on refresh as well.

Collapse
 
sabrinatahir profile image
Sabrina-tahir

i have the same issue how did u solve it?

Collapse
 
majdghithan profile image
Majd

Yes, you can use react-guards to check if user is logged in or not. Then implement the routing

Collapse
 
majdghithan profile image
Majd

Hello, you are right. This may be risky. How to get around this problem?

Collapse
 
rahulnikam profile image
Rahul Nikam

you can store it in cookie.

Collapse
 
bhavindhodia profile image
Bhavin Dhodia

For more security you can you React Context hooks to tokens in memory and then validate it every few minutes... I tried implementing it from this blog.

Collapse
 
sabrinatahir profile image
Sabrina-tahir

I have the same problem , have u got your problem solved?

Collapse
 
ahmad_azizi95 profile image
Ahmad Azizi

I think this would be resolved if you keep the "isAuthenticated" in redux store and implement redux-persist, on refresh redux-persist would simply rehydrate your store. Clearing the local storage would clear the persisted store as well (logout functionality)

Collapse
 
junioradadie profile image
Adadie Junior

You could add a token verification function in the route protection helper which would be true depending on the response from your API

Collapse
 
leeuw profile image
leeuw • Edited

Hey! I think the best way to do authentication in react is to create a context using react createContext with a custom hook for example useAuth, then wrap the App component which exist in the index.js with that context. this way is more safer.

Collapse
 
joshuaolusayo profile image
Joshua Oyeleke

What I have been struggling with for weeks got solved under 5minutes. Thank you very much for this. You just saved my life

Collapse
 
spiropoulos94 profile image
NikosSp

Hello i have a very important question to make, I had no luck searching it online for the past few days.

Can we just wrap all the restricted routes under a protected route component? and just render them as { children } if a condition is met?

Collapse
 
priyeduye profile image
Priye

This is helpful, thanks 😊

Collapse
 
yvan99 profile image
Ishimwe Yvan

very helpful , was stuck on this bug for a while and boom !!, i got your article

Collapse
 
yhomi profile image
Kareem Yomi

Nice one bruh..

Please do token expire sometimes

Collapse
 
olumidesamuel_ profile image
OlumideSamuel

Okay, will look into that

Collapse
 
mohamad_el_bohsaly profile image
Mohamad El Bohsaly

What is the use of useDispatch method?

Collapse
 
mrzaizai2k profile image
Mai Chi Bao

wow that is so detail and simple to understand the role of protected route

Collapse
 
ilaroy611 profile image
ILA Saxena

is this going to work for react-router-dom V6 ?