CodingTricks LogoCodingTricks
HomePostsTipsCopy/PasteLinksContact UsAbout Us
2024 - 2025 CodingTricks.co | All rights reserved
Privacy PolicyTerms of Service
Implementing Roles and Permissions in your Next js 14: An Overview

Implementing Roles and Permissions in your Next js 14: An Overview

Posted by

kamlesh paul

on

Dec 9, 2024

| 5 min read

Last updated on : Dec 9, 2024

Authentication
764 views

Managing Roles and permissions in your Next js 14 application is crucial for controlling access to various parts of your app. This article provides a high-level overview, allowing you to implement these concepts regardless of the authentication method or database you are using. For a detailed, step-by-step guide, stay tuned for our in-depth article.

We’ll cover how to implement Role-Based Access Control (RBAC) in your Next js 14 application. The focus will be on:

  1. Protecting Pages and Components: Learn how to restrict access to specific pages or components based on user roles and permissions.
  2. Using a Guard Component: Implement a component that conditionally renders content based on the user’s permissions.
  3. Helper Functions for Permission Management: Utilize functions to fetch user permissions and check for specific access rights.
  • What Needs to be Protected:
    • Pages: Restrict access to certain routes based on the user’s role and permissions.
    • Components: Show or hide components depending on whether the user has the required permissions.

#Table of contents

  • Database Structure
  • Implementing Role-Based Access Control
    • Guard Component
    • Usage
    • Helper Functions
    • Protect Pages and redirect
  • Conclusion

#Database Structure

To get started, you’ll need to set up your database with the following tables:

  • users
  • roles
  • permissions
  • role_permissions

table sturture of - Roles-and-permissions-in-your-Next-js-14.webp

You can use any database to store this data. Here’s the basic idea:

  • Users table: Each user can have one role.
  • Roles table: Roles can have many permissions.
  • Role_Permissions table: This pivot table links roles to permissions.

Here’s a basic example of inserting roles and permissions:

const roles = await db.insert(Schema.rolesTable).values([
  { name: 'admin' },
  { name: 'editor' },
  { name: 'user' },
]).returning();
 
 
const permissions = await db.insert(Schema.permissionsTable).values([
  { name: 'post-list' },
  { name: 'post-create' },
  { name: 'post-edit' },
  { name: 'post-delete' }
]).returning();
 
const getRoleId = (roleName) => {
  const role = roles.find(role => role.name === roleName);
  if (!role) {
    throw new Error(`Role ${roleName} not found`);
  }
  return role.id;
};
 
const getPermissionId = (permissionName) => {
  const permission = permissions.find(permission => permission.name === permissionName);
  if (!permission) {
    throw new Error(`Permission ${permissionName} not found`);
  }
  return permission.id;
};
 
const rolePermissions = [
  { role_id: getRoleId('admin'), permission_id: getPermissionId('post-list') },
  { role_id: getRoleId('admin'), permission_id: getPermissionId('post-create') },
  { role_id: getRoleId('admin'), permission_id: getPermissionId('post-edit') },
  { role_id: getRoleId('admin'), permission_id: getPermissionId('post-delete') },
 
  { role_id: getRoleId('editor'), permission_id: getPermissionId('post-create') },
  { role_id: getRoleId('editor'), permission_id: getPermissionId('post-edit') },
 
  { role_id: getRoleId('user'), permission_id: getPermissionId('post-list') }
];
 
await db.insert(Schema.rolePermissionsTable).values(rolePermissions);
await db.insert(Schema.usersTable).values([
  { name: 'Admin', email: 'admin@example.com', password: await makePassword('password'), role_id: getRoleId('admin') },
  { name: 'Editor', email: 'editor@example.com', password: await makePassword('password'), role_id: getRoleId('editor') },
  { name: 'User', email: 'user@example.com', password: await makePassword('password'), role_id: getRoleId('user') }
]);

#Implementing Role-Based Access Control

  • Define a list of permissions in a constant file:
permssions.ts
export const permissionList = {
  POST_SHOW: 'post-list',
  POST_CREATE: 'post-create',
  POST_EDIT: 'post-edit',
  POST_DELETE: 'post-delete'
// add more as per need, it should match with permissions table names
};

#Guard Component

  • create a Guard component to hide and show child components based on the logged-in user’s permissions:
components/Guard.tsx
"use server";
 
import { getMyPermissions } from "@/server/queries/user";
import { ReactNode } from "react";
 
interface GuardProps {
  permissions: string[];
  children: ReactNode;
}
 
export default async function Guard({ permissions, children }: GuardProps) {
  const myPermissions = await getMyPermissions();
 
  const hasPermissions = permissions.every(permission => 
    myPermissions.some(x => x.permissionName === permission)
  );
 
  if (!hasPermissions) {
    return null;
  }
 
  return (
    <>
      {children}
    </>
  );
}
  • This is a server component, so it won’t show any UI shift on the frontend and it is more secure as it is server side.

#Usage

<Guard
  permissions={[permissionList.POST_DELETE]}
>
  <Button>
    Delete Post
  </Button>
</Guard>

Like this you can hide.

  • Now you need to protect route as well right let cover this as well.

#Helper Functions

To fetch permissions for the logged-in user:

"use server";
 
 
export const getMyPermissions =async () => {
  const session = await auth(); // next-auth
  if (!session?.user?.role_id) {
    throw new Error("Role id not found in session.");
  }
 
  const permissions = await db
    .select({
      permissionName: permissionsTable.name
    })
    .from(rolePermissionsTable)
    .innerJoin(permissionsTable, eq(rolePermissionsTable.permission_id, permissionsTable.id))
    .where(eq(rolePermissionsTable.role_id, session.user.role_id));
 
  return permissions;
};
  • To check if the user has a specific permission:
"use server"
 
import { getMyPermissions } from "@/server/queries/user";
import { cache } from "react";
 
export const hasPermission = cache(async (permissions) => {
  const myPermissions = await getMyPermissions();
  return permissions.every(permission => 
    myPermissions.some(x => x.permissionName === permission)
  );
});

#Protect Pages and redirect

import { permissionList } from '@/utils/constants';
import { hasPermission } from '@/utils/guard';
import { redirect } from 'next/navigation';
import React from 'react';
 
export default async function UserPage() {
  const permission = await hasPermission([permissionList.POST_SHOW]);
  if (!permission) {
    return redirect('/');
  }
 
  return (
    <div>You have User page access</div>
  );
}

#Conclusion

This is a high-level overview of how you can set up roles and permissions in a Next js application. You can use this setup no matter what authentication method or database you are using. For a detailed, step-by-step guide, Click here

By following these basic steps, you can ensure that your Next js application has a robust role and permission management system.

Github url https://github.com/Kamleshpaul/nextjs-role-and-permission

Related Posts

  • How to Add Laravel Passkeys to Laravel 11 How to Add Laravel Passkeys to Laravel 11
  • Lucia-auth is Deprecated: Meet the Better Alternative – Better AuthLucia-auth is Deprecated: Meet the Better Alternative – Better Auth
  • How to Add Biometric Authentication Login in Next.js (WebAuthn Nextjs in App Router)How to Add Biometric Authentication Login in Next.js (WebAuthn Nextjs in App Router)
  • Nextjs 14 roles and permissions (RBAC) : Step-by-Step GuideNextjs 14 roles and permissions (RBAC) : Step-by-Step Guide

Tags

Api(1)Authentication(5)Backup (1)Copy Paste(12)Email(2)Express(1)Firebase(1)Github Action(2)News(8)Push Notification(1)Queue(2)Server(11)Server Action(3)Testing(1)Tips(17)Websocket(1)

Popular Posts

  • TweakPHP 0.1.0 Beta: A Free and Open-Source Alternative to Tinkerwell Is Here!  TweakPHP 0.1.0 Beta: A Free and Open-Source Alternative to Tinkerwell Is Here!
  • How to use WebSocket in NextJS App router with Socket.IOHow to use WebSocket in NextJS App router with Socket.IO
  • How to Set Up Queue Jobs in NextJS Using BullMQHow to Set Up Queue Jobs in NextJS Using BullMQ
  • Boost Laravel Performance: Running Octane with FrankenPHP in Production ( Zero downtime)Boost Laravel Performance: Running Octane with FrankenPHP in Production ( Zero downtime)
  • How to Set Up NextJS cron jobs without VercelHow to Set Up NextJS cron jobs without Vercel
  • Mastering Laravel Streamed Responses: Boost Performance with Fast Data DeliveryMastering Laravel Streamed Responses: Boost Performance with Fast Data Delivery
  • Tinkerwell Alternative: Free and Open-Source PHP Debugging with TweakPHPTinkerwell Alternative: Free and Open-Source PHP Debugging with TweakPHP
  • Nextjs 14 roles and permissions (RBAC) : Step-by-Step GuideNextjs 14 roles and permissions (RBAC) : Step-by-Step Guide

Get updates directly to your inbox.

Join 500+ developers getting updates on Laravel & Next.js tips. No spam,
unsubscribe anytime.


Share this article:

764 views