Company Logo

Bisht Bytes

How to Use CASL in Next.js for Role Based Access Control

Published On: 01 Oct 2024
Reading Time: 9 minutes

Overview


In modern web applications, managing permissions and user roles is critical for ensuring the right people have access to the right resources. Whether you’re building a small project or a large-scale application, implementing Role-Based Access Control (RBAC) efficiently can save you from security risks and unnecessary complexity.

Enter CASL (Code Access Security Library) — a powerful and flexible JavaScript library designed to manage permissions and roles. CASL simplifies the process of defining what actions a user can perform in your application. In this article, we'll dive into why CASL is an excellent choice for Next.js and how you can integrate it into your Next.js app.

Reference: https://casl.js.org/v6/en/

Why Use CASL in Your Next.js App?

1. Granular Permission Control
CASL allows you to define fine-grained permissions, going beyond just restricting access to routes. You can manage what actions users can perform on specific resources, such as reading, creating, updating, or deleting. This is particularly useful in Next.js where different parts of the page might need to display differently depending on the user's role or permissions.

2. Declarative Syntax
CASL provides a highly readable, declarative API. You define what users can do in plain English, making it easy to understand and maintain permissions logic.

3. Server-Side and Client-Side Support
Next.js is a full-stack framework that combines both server-side rendering (SSR) and client-side rendering. CASL works seamlessly in both environments, ensuring that your permission rules are enforced consistently whether you’re dealing with API routes, SSR pages, or client-rendered pages.

4. Flexibility and Extensibility
You can use CASL for basic role-based access or extend it to handle more complex scenarios, such as conditional permissions or access based on dynamic data.

How to Use CASL in a Next.js App

Step 1: Install CASL

To start using CASL in your Next.js project, install the necessary packages:

npm install @casl/ability @casl/react
  • @casl/ability: The core library for defining and checking abilities.
  • @casl/react: Provides React hooks and components to easily work with CASL in React-based apps like Next.js.

Step 2: Define Your Abilities

Create a file to define the user permissions (abilities) based on their role. This can be done in a utility file (e.g., casl/abilities.ts).

import { AbilityBuilder, Ability } from '@casl/ability';

// Define action types
type Action = 'manage' | 'read' | 'update';

// Define subject types
type Subject = 'Post' | 'all';

export function defineAbilitiesFor(user: { role?: string }) {
  const { can, cannot, rules } = new AbilityBuilder<Ability<[Action, Subject]>>(Ability);

  if (user?.role === 'admin') {
    can('manage', 'all');  // Admins can do everything
  } else if (user?.role === 'editor') {
    can('read', 'all');  // Editors can read everything
    can('update', 'Post');  // Editors can update posts
  } else {
    can('read', 'Post');  // Regular users can only read posts
  }

  return new Ability(rules);
}

In this example, we’re setting up three roles: admin, editor, and a regular user. Admins can perform any action (manage all), editors can read and update posts, while regular users can only read posts.

Step 3: Provide Abilities in the App

To make the abilities available across your Next.js app, use React’s context to inject abilities into the component tree. Create an AbilityContext to wrap your components.

import React, { createContext } from 'react';
import { Ability } from '@casl/ability';

export const AbilityContext = createContext<Ability>(new Ability([]));

export function AbilityProvider({ user, children }) {
  const ability = defineAbilitiesFor(user);

  return (
    <AbilityContext.Provider value={ability}>
      {children}
    </AbilityContext.Provider>
  );
}

Then, in your app/layout.tsx or page component, wrap your application with AbilityProvider:

import { AbilityProvider } from '../casl/abilities';

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const { user } = pageProps;

  return (
    <AbilityProvider user={user}>
      {children}
    </AbilityProvider>
  );
}

export default MyApp;

This way, all your pages have access to the ability context and can enforce permissions based on user roles.

Step 4: Enforce Permissions in Components

To conditionally render components or restrict access based on abilities, use the Can component from @casl/react.

import { Can } from '@casl/react';
import { useContext } from 'react';
import { AbilityContext } from '../casl/abilities';

function Post({ post }) {
  const ability = useContext(AbilityContext);

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>

      {/* Only show the edit button if the user can update the post */}
      <Can I="update" a="Post" ability={ability}>
        <button>Edit Post</button>
      </Can>
    </div>
  );
}

export default Post;

In the example above, the edit button is only rendered if the user has permission to update a post. If the user is an admin or an editor, they will see the button. Otherwise, it will be hidden.

Step 5: Protect Server-Side Resources

CASL can also be used on the server side in Next.js API routes handler for SSR pages. For instance, you can use it in an API route to prevent unauthorized users from performing sensitive operations.

// app/api/posts/update/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { defineAbilitiesFor } from '@/casl/abilities';

export async function PUT(req: NextRequest) {
  const user = getUserFromSession(req); 
  const ability = defineAbilitiesFor(user);

  if (ability.cannot('update', 'Post')) {
    return NextResponse.json({ message: 'Forbidden' }, { status: 403 });
  }

  
  const postData = await req.json();

  return NextResponse.json({ message: 'Post updated successfully' }, { status: 200 });
}

By checking abilities in your API route, you ensure that only authorized users can perform sensitive actions such as updating a post.

Conclusion

CASL is an invaluable tool when it comes to implementing role-based access control in Next.js applications. It allows you to define clear and maintainable rules for what different users can and cannot do. Whether you need simple role management or more complex permission logic, CASL offers the flexibility to handle both.

By integrating CASL into your Next.js app, you can:

  • Implement granular permission checks on both the client and server.
  • Protect sensitive data and resources from unauthorized users.
  • Ensure your app is secure and scalable as it grows.

Using CASL's declarative syntax and comprehensive feature set will not only improve your codebase’s security but also keep it clean and maintainable.


Page Views: -