How to Set Router Authentication in React

Rana Hasnain Khan Feb 02, 2024
How to Set Router Authentication in React

We will introduce protected routes and authentication with react-router in React application.

React Router Authentication in React

When building a commercial web app or a website in React, we often need to hide certain routes in our application from users who are not logged in or do not have a certain user role required to access those routes.

React router provides us with protected routes; they let us choose which routes users can visit based on their user roles or whether they are logged in or not.

For example, we have a home, about, contact, and login page that all users can access. But protected routes such as dashboard and profile settings can only be accessed by logged-in users.

React router does not provide any functionality for this, but it is very easy and straightforward to add this functionality. First, we need to add functionality to authenticate the user to start working on protected routes.

We will use a fake AuthUser Hook to check the authentication status.

Let’s create a new application by using the following command.

# react
npx create-react-app my-app

After creating our new application in React, we will go to our application directory using this command.

# react
cd my-app

Now, let’s run our app to check if all dependencies are installed correctly.

# react
npm start

We will create a new file, AuthUser.js, in the’ src’ folder. Once we have created a new file, now we will create a context and assign it to a constant authUserContext.

# react
const authUserContext = React.createContext();

Once we created a context, now we will create a function AuthUser that will set the states of 2 variables to authenticate and setAuthenticate to false that we will use to check if the user is authenticated or not.

And we will create 2 methods in return login() and logout(). login() will set the value of setAuthenticate to true and logout() will set it back to false.

Now, we will export the function AuthenticationProvider that will save AuthUser() in a constant auth. So our code in AuthUser.js will look like below.

import * as React from "react";

const authUserContext = React.createContext();

function AuthUser() {
  const [authenticate, setAuthenticate] = React.useState(false);

  return {
    authenticate,
    login() {
      return new Promise((res) => {
        setAuthenticate(true);
        res();
      });
    },
    logout() {
      return new Promise((res) => {
        setAuthenticate(false);
        res();
      });
    }
  };
}

export function AuthenticationProvider({ children }) {
  const auth = AuthUser();

  return (
    <authUserContext.Provider value={auth}>{children}</authUserContext.Provider>
  );
}

export default function AuthenticateConsumer() {
  return React.useContext(authUserContext);
}

Whenever we want to use authenticate, login(), and logout(), we can use the AuthUser Hook. We will start building the navigation that will contain 5 components Home, About, Profile, Login, Dashboard.

Home, About, and Login will be publicly accessible, while Dashboard and Profile can only be accessed if the user is authenticated.

First of all, we will import Link, Routes, Route, UseNavigate and userLocation from react-router-dom. We will also import AuthUser from the new file we created.

# react
import * as React from "react";
import {
  Link,
  Routes,
  Route,
  useNavigate,
  Navigate,
  useLocation
} from "react-router-dom";
import AuthUser from "./AuthUser";

Now, we will define constants for our navigations displayed when we visit a certain navigation link.

If it is a publicly accessible link, it will display the public route, and if it is a private link, it will display a private route only if the user is authenticated. Otherwise, it will be redirected to a login page.

# react
const Home = () => <h1>Home (Public Route)</h1>;
const About = () => <h1>About (Public Route)</h1>;

const Dashboard = () => <h1>Dashboard (Private)</h1>;
const Profile = () => <h1>Profile (Private)</h1>;

Now we will create a constant for the Login method in which we will log in user, and after authenticating, we will redirect it to the dashboard.

If the user is not logged in, it will return a login page content that will be displayed whenever a user without authentication tries to access the private pages.

const Login = () => {
  const navigate = useNavigate();
  const { login } = AuthUser();
  const { state } = useLocation();

  const handleLogin = () => {
    login().then(() => {
      navigate(state?.path || "/dashboard");
    });
  };

  return (
    <div>
      <h1>Login</h1>
      <button onClick={handleLogin}>Log in</button>
    </div>
  );
};

Now we will create a navigation function in which we will return a navigation bar with all the routes, including both public and private routes, with a login button if the user is not authenticated and a logout button that will display if the user is authenticated.

We will also create 2 methods, handleLogout and handleLogin.

function Nav() {
  const { authenticate, logout, login } = AuthUser();
  const navigate = useNavigate();

  const handleLogout = () => {
    logout();
    navigate("/");
  };
  const handleLogin = () => {
    login();
    navigate("/dashboard");
  };

  return (
    <nav>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/about">About</Link>
        </li>
        <li>
          <Link to="/dashboard">Dashboard</Link>
        </li>
        <li>
          <Link to="/profile">Profile</Link>
        </li>
        <li>{!authenticate && <button onClick={handleLogin}>Login</button>}</li>
      </ul>
      {authenticate && <button onClick={handleLogout}>Logout</button>}
    </nav>
  );
}

We will create a function RequireAuthentication that will authenticate the user whenever a user tries to access private routes.

function RequireAuthentication({ children }) {
  const { authenticate } = AuthUser();
  const location = useLocation();

  return authenticate === true ? (
    children
  ) : (
    <Navigate to="/login" replace state={{ path: location.pathname }} />
  );
}

In our App() function, we will define our routes.

export default function App() {
  return (
    <div>
      <Nav />

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route
          path="/dashboard"
          element={
            <RequireAuthentication>
              <Dashboard />
            </RequireAuthentication>
          }
        />
        <Route
          path="/profile"
          element={
            <RequireAuthentication>
              <Profile />
            </RequireAuthentication>
          }
        />
        <Route path="/login" element={<Login />} />
      </Routes>
    </div>
  );
}

Let’s test our app and check how it works. See the live demo here.

Output:

react router authentication example

As you can see in the above example that it is quite easy to set authenticated routes, but this AuthUser Hook we used was just a frontend check.

But if we are working on a proper web app, we need to get the user authenticated in the backend to secure our system.

Rana Hasnain Khan avatar Rana Hasnain Khan avatar

Rana is a computer science graduate passionate about helping people to build and diagnose scalable web application problems and problems developers face across the full-stack.

LinkedIn

Related Article - React Router