How to Protect Routes for Admins in React Next.js Using HOC

The SaaS Enthusiast
5 min readDec 15, 2023

--

Introduction

In a previous piece, we delved into the significance of safeguarding routes for authenticated users in React applications, highlighting Higher Order Components (HOC) as an optimal solution. Recently, I encountered a new challenge: ensuring certain components are accessible exclusively to a specific group of users. This article will guide you through implementing this using React Next.js and AWS Cognito.

In my previous endeavors, I’ve adopted a proactive approach by assigning a default profile attribute with the value user to every new user in AWS Cognito. This foresight now pays off, allowing me to accommodate the new requirement seamlessly, without the need for a complex migration.

Reviewing the Case

Our scenario involves a React frontend interfacing with AWS services in the backend. Using AWS Cognito, I’ve established a user pool to manage application users. The goal is to create a mechanism where only authenticated users with an admin profile can access specific content. There are several strategies to achieve this:

  1. URL Obscurity: One approach is to obscure the URL, ensuring there are no direct links to it within the application, thus making it accessible only to admins.
  2. Attribute Verification: Another method involves verifying the user’s profile attribute during component initialization. Utilizing the useEffect hook, we can check the user's metadata for the admin profile. If the check fails, the user is redirected to the login page.
  3. Reusable HOC: My preferred method is implementing a Higher Order Component. This HOC wraps around any component that requires protection, checking for the appropriate user profile. This approach is not only sustainable but also scalable, making it ideal for larger applications.

Engaging the Audience

Before diving into the technical details, let’s address why this approach matters. In the dynamic world of web development, security and user experience often go hand-in-hand. By ensuring that only certain users can access specific routes, we not only bolster security but also create a more personalized and relevant experience for our users.

Implementation

1. Creating the Higher Order Component (HOC)

Conceptual Overview

An HOC in React is essentially a function that takes a component and returns a new component with additional properties or logic. For our use case, the HOC will check the user’s profile attribute and determine if they're an admin.

Step-by-Step Guide

a. Define the HOC: Create a new JavaScript file for the HOC, say withAdminProtection.js. This HOC should take a component (WrappedComponent) as an argument.

b. Implement Authentication Logic: Inside the HOC, implement logic to interact with AWS Cognito. Fetch the current user’s profile and determine if they have an admin role.

c. Conditional Rendering: Based on the user’s role, either render the WrappedComponent or redirect non-admin users to a designated page, such as the homepage or a 'not authorized' page.

import React, {useEffect} from 'react';
import { useRouter } from 'next/router';
import {useUser} from "./UserContext";

const AdminRoute = (Component) => {
return (props) => {
const {user, isAuthenticating} = useUser();
const isAdmin = user?.attributes?.profile === "admin";
const router = useRouter();

useEffect(() => {
if (!isAdmin && !isAuthenticating) {
console.log(user?.attributes);
// Redirect to home page or login page if not admin
router.push('/');
}
}, [isAdmin, router]);

// Render the component if the user is an admin
return isAdmin ? <Component {...props} /> : null; // Or a loading indicator or a "not authorized" message
};
};

export default AdminRoute;

2. Wrapping Components in Protected Routes

Tailored Protection

While the global integration in _app.js provides a broad security net, there might be scenarios where specific components or pages require admin protection.

How to Wrap Specific Components

a. Import the HOC: In any component or page that requires admin access, import the withAdminProtection HOC.

b. Apply the HOC: Wrap the export of the component with the HOC. For example, export default withAdminProtection(MyComponent); This ensures that only users with an admin profile can access MyComponent.

Considerations

  • User Experience: Ensure that the redirection for non-admin users is handled gracefully, providing a clear message or pathway back to permitted areas.
  • Performance: Test the application for any performance impacts due to the additional checks and redirects.
  • Security: Regularly review and update the authentication logic to align with best security practices and any changes in AWS Cognito.
import React from 'react';
import MsaTitle from "../src/components/MsaTitle";
import {useUser} from "../src/context/UserContext";
import AdminRoute from "../src/context/AdminRoute";

function Control() {
const user = useUser();
return (
<div>
Hello Admin
</div>
);
}

export default AdminRoute(Control);

Conclusion: The Long-Term Benefits of Thoughtful Solutions

In the realm of software development, the quest for efficient solutions often presents a dichotomy: opting for a quick fix or investing time in a robust, scalable solution. Through the journey of securing admin-only routes in a React Next.js application using Higher Order Components (HOCs), we’ve embraced the latter.

This approach, while initially more time-intensive, has undeniably proven its worth. The beauty of this method lies not just in its immediate effectiveness, but in the far-reaching benefits it bestows:

  1. Deepened Understanding: Tackling challenges with comprehensive solutions enhances our understanding of the technologies and concepts at hand. This process transforms every project into a learning opportunity, paving the way for more informed decision-making in future endeavors.
  2. Adaptability and Scalability: By embedding a level of flexibility into our HOC solution, we’ve created a tool that’s not just effective for our current needs but is readily adaptable to future requirements. Whether it’s modifying user access levels or extending functionality, changes can be implemented globally with minimal adjustments.
  3. Reusable Wisdom: Perhaps one of the most significant advantages of this approach is the reusability of our solution. The knowledge and code we’ve developed can be seamlessly integrated into other projects, especially where similar business requirements arise. This not only saves time but also ensures consistency across different applications.
  4. Preparedness for Complexity: In software development, simplicity often lays the groundwork for handling complexity. By mastering the art of creating versatile HOCs for route protection, we equip ourselves to tackle more complex scenarios with confidence.

In conclusion, the path to mastering new techniques and implementing robust solutions might require a bit more time and effort upfront. However, the payoff is immediate and multi-faceted. We gain not just a solution for the present but a toolkit for the future, enriched knowledge, and the agility to adapt to varying project needs. Let’s continue to embrace these deeper dives into technology, for they are the bedrock upon which innovative and sustainable software is built.

What’s Next?

Link to my previous article: How to protect routes for Authenticated users in Next.js (or any React Application)

Other Articles You May Like

New Projects or Consultancy

Protecting Routes

Advanced Serverless Techniques

Mastering Serverless Series

--

--