Demystifying authentication flows in React

Lakshay Batra
affinityanswers-tech
4 min readNov 28, 2023

As a Front End Engineer at AffinityAnswers, I handle authentication in various products. For complex projects, we rely on external libraries like Auth0. In this blog, I’ll share two straightforward and effective strategies I use for handling authentication in React. Let’s dive in!

Introduction
Authentication is a crucial aspect of web development, especially when building dynamic and interactive applications. In the world of React, a popular JavaScript library for building user interfaces, understanding how authentication works is essential for creating secure and user-friendly experiences. In this blog post, we’ll demystify authentication in React, exploring the key concepts, common strategies, and best practices to implement a robust authentication system.

Prerequisites
Basic understanding of
1. React
2. State management using React Context
3. Routing using React Router

Basic Setup
In this guide, we’ll swiftly craft two React pages and their routes:
1. Sociogram page, route as “/”
2. Buyscape page, route as “/buyscape”
Following that, we’ll establish a React context to manage the authentication state. Once that foundation is set, we’ll create the Login page accessible at “/login”. The example here has been taken from an internal application Audience Cohort Platform aka ACP which is used by Data Analysts at AffinityAnswers to derive relevant insights for the Client.

/* Sociogram.js */

export default function Sociogram() {
return (
/* Here goes the content for Sociogram,
which provides insights based on Social data */
<h1>Welcome to Sociogram</h1>
);
}
/* Buyscape.js */

export default function Buyscape() {
return (
/* Here goes the content for Buyscape,
which provides insights based on Purchase data */
<h1>Welcome to Buyscape</h1>
);
}

Basic setup of React context for managing the state of user authentication

/* contexts/AuthContext.js */

import { createContext, useState } from "react";

export const AuthContext = createContext();

export function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(false);

return (
<AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
{children}
</AuthContext.Provider>
);
}
/* index.js */

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter as Router } from "react-router-dom";

import App from "./App";
import { AuthContext, AuthProvider } from "./contexts/AuthContext";

export { AuthContext };

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
<StrictMode>
<Router>
<AuthProvider>
<App />
</AuthProvider>
</Router>
</StrictMode>,
);

Creating a basic Login page

/* Login.js */

import { useContext } from "react";
import { useLocation, useNavigate } from "react-router";
import { AuthContext } from "..";

export default function Login() {
const { setIsLoggedIn } = useContext(AuthContext);
const navigate = useNavigate();
const location = useLocation();
const handleLogin = () => {

/* Perform here the logic to authenticate the user
and set the state of login */

setIsLoggedIn(true);

/* Code below will take user to the same route from where
redirect to /login happened */

if (location?.state?.from?.pathname)
navigate(location?.state?.from?.pathname);
else navigate("/");
};
return (
<div>
Login Page <button onClick={handleLogin}>login</button>
</div>
);
}

The above outlines the fundamental steps for a basic setup, let’s see in action the two effective ways of handling authentication:

1. Creating a higher-order component to be wrapped around the components to be protected.
In React, a Higher-Order Component (HOC) is a design pattern that involves wrapping a component with another component to enhance or modify its behavior

/* components/RequiresAuth.js */

import { useContext } from "react";
import { Navigate, useLocation } from "react-router-dom";

import { AuthContext } from "..";

export default function RequiresAuth({children}){
const {isLoggedIn} = useContext(AuthContext)
const location = useLocation()
/* Code below will check for the state of login and take the
action upon the same */
if(isLoggedIn) return children
return <Navigate to="/login" state={{from:location}} />
}

Wrapping the Buyscape component to be protected

/* App.js */

import "./styles.css";
import { useNavigate } from "react-router";
import { Routes, Route, NavLink } from "react-router-dom";

import Login from "./Login";
import Sociogram from "./Sociogram";
import Buyscape from "./Buyscape";
import RequiresAuth from "./components/RequiresAuth";

export default function App() {

/* <Buyscape /> component will be wrapped under the
<RequiresAuth> component we created above to handle auth logic */

return (
<>
<div>
<NavLink to="/">Sociogram</NavLink> ||{" "}
<NavLink to="/buyscape">Buyscape</NavLink>
</div>
<Routes>
<Route path="/" element={<Sociogram />} />
<Route
path="/buyscape"
element={
<RequiresAuth>
<Buyscape />
</RequiresAuth>
}
/>
<Route path="/login" element={<Login />} />
</Routes>
</>
);
}

2. Creating a higher-order component to be executed within the component to be protected.

/* components/withProtectedRoute.js */

import { useContext } from "react";
import { Navigate, useLocation } from "react-router-dom";

import { AuthContext } from "..";

const withProtectedRoute = (WrappedComponent) => {

const ProtectedRoute = (props) => {
const location = useLocation();
const { isLoggedIn, setIsLoggedIn } = useContext(AuthContext);

if (isLoggedIn) {
/* If the user is authenticated,
render the wrapped component with its props */
return <WrappedComponent {...props} />;
} else {
/* If the user is not logged-in, redirect to a login page */
return <Navigate to="/login" state={{ from: location }} />;
}
};

return ProtectedRoute;
};

Since I employed Auth0 for ACP, I leveraged the withAuthenticationRequired function it provides, which essentially operates as described earlier.

Calling withProtectedRoute inside Sociogram.js to protect it

/* Sociogram.js */

import withProtectedRoute from "../components/withProtectedRoute"

function Sociogram() {
return (
/* Here goes the content for Sociogram,
which provides insights based on Social data */
<h1>Welcome to Sociogram</h1>
);
}

export default withProtectedRoute(Sociogram)

Conclusion
Demystifying authentication in React involves grasping the key concepts, understanding authentication flows, and implementing secure strategies. By following best practices and leveraging React’s capabilities, developers can build robust and user-friendly authentication systems, ensuring the security and integrity of their applications. Whether you’re new to React authentication or looking to enhance your existing knowledge, a solid understanding of these concepts is vital for creating successful web applications.

--

--