Implementing Role-Based Access Control in Server Actions - By Sourav Mishra (@souravvmishra)

Stop manually checking user roles in every function. Learn how to build a type-safe RBAC wrapper for your Next.js Server Actions.

BySourav Mishra2 min read

Security in Next.js Server Actions is critical. A common mistake is relying on the UI to hide buttons, but forgetting to secure the actual server function.

In this guide, I, Sourav Mishra, Co-founder of Codestam Technologies, share my preferred pattern for robust Role-Based Access Control (RBAC) that protects all our enterprise applications.

The Manual Way (Repetitive & Unsafe)

export async function deleteProduct(id: string) {
  const session = await auth();
  if (!session || session.user.role !== 'ADMIN') {
    throw new Error('Unauthorized');
  }
  // ... delete logic
}

If you forget that if block once, your app is vulnerable.

The Higher-Order Function (HOF) Pattern

Let's create a wrapper that handles this logic centrally.

1. Create the Wrapper

// lib/safe-action.ts
import { auth } from './auth';

type Role = 'ADMIN' | 'USER';

export function authenticatedAction(
  allowedRoles: Role[],
  action: Function
) {
  return async (...args: any[]) => {
    const session = await auth();
    
    if (!session?.user) {
      throw new Error('Not authenticated');
    }

    if (!allowedRoles.includes(session.user.role)) {
      throw new Error('Forbidden');
    }

    return action(...args);
  };
}

2. Use It Everywhere

Now, writing secure actions is cleaner and safer.

// actions/products.ts
import { authenticatedAction } from '@/lib/safe-action';

export const deleteProduct = authenticatedAction(
  ['ADMIN'], 
  async (id: string) => {
    await db.product.delete({ where: { id } });
  }
);

At Codestam Technologies, we take this a step further by defining granular permissions (e.g., product:delete, user:view) instead of just roles. This allows us to create custom roles for clients without changing the code.

Benefits

  1. D.R.Y (Don't Repeat Yourself): Auth logic is written once.
  2. Auditability: You can see exactly which potential roles access which function.
  3. Type Safety: You can extend the HOF to infer input types (using Zod).

Key Takeaways

  • Never trust the client: Just because a button is hidden doesn't mean the action is unreachable.
  • Centralize Auth: Use wrappers/middleware logic for consistency.

For more security tips, ensure your middleware isn't leaking data by reading Middleware Anti-Patterns.


This guide was written by Sourav Mishra, Co-founder of Codestam Technologies and a Full Stack Engineer.

Share this post

Cover image for Implementing Role-Based Access Control in Server Actions

You might also like

See all