A Complete Guide to Next.js Authorization

Published by Rohit Ghumare on April 19, 2024
A Complete Guide to Next.js Authorization

In this tutorial, we’ll learn about authorization, how it works, and how to integrate it into a Next.js application using Cerbos, an open-source authorization project. This approach will allow us to seamlessly manage and enforce access controls within our web application. Let's get started.

What is Authorization?

Authorization grants or denies access to resources based on user type, setting rules for user actions within the app. Authorization defines the criteria for what actions a user can carry out within an application. For instance, consider a blogging application where a user attempts to edit a blog. The authorization mechanism checks if the user is the owner of that particular blog post. If the user is not the owner, access to edit the blog is denied, ensuring that users can only edit their own blog posts.

What is Access Control?

Access control is crucial for managing authorization within applications, particularly in ensuring that only authorized users can access certain functionalities. In the context of our blogging application, which consists of five users (User1, User2, User3, User4, and User5), access control helps define and enforce who can edit or delete blog posts.

In our application, we want to implement a specific access control policy:

  • Admin Users: User2 and User5 are designated as admin users. They can edit and delete any blog post within the application, regardless of who the original poster was.
  • Regular Users: User1, User3, and User4 are regular users. These users are restricted to editing or deleting only blog posts that they have created.

Challenges Faced By Developers in Implementing Access Control

  1. Complicated Logic Repetition: Replicating access control logic across different environments, whether cloud-based or on-premises, requires careful coordination by developers. As synchronizing the logic gets more complicated, the likelihood of introducing inconsistencies grows.
  2. Distributed Audit Logs: Maintaining and tracking audit logs becomes challenging in any system architecture due to the spread of logs across multiple systems. This issue creates a disconnect between those who formulate policies and those who implement them, complicating the management of audits and policy updates.
  3. Scalability Issues: As systems grow, scaling access control mechanisms efficiently becomes a critical challenge, highlighting the necessity of balancing security with operational efficiency across all types of systems and patterns.

Where and How Does Cerbos Fit into the Authorization Ecosystem?

Decoupled Authorization

Cerbos, as a stand-alone open-source project, streamlines access control by centralizing policy decisions and audit log collection, eliminating the need for replicating rules across components. SDKs for the most popular programming languages and a straightforward API for integrating with any other software make it flexible and adaptable. Cerbos can function effectively within diverse software environments, including decentralized architectures. And it has multiple deployment options, including a Kubernetes sidecar and a Docker container, which allow it to scale seamlessly with other services. 

Integrating Cerbos in a Next.js Application

In our blogging platform built in Next.js, we have five users, and we want to give admin permission to user2 and user5, giving them the authority to update and delete all the blogs irrespective of the author. As of now, the way our blogging applications work is that nobody can edit or delete anyone else's posts. We need some policy or authorization mechanism. Below is a typical example of how our permission should look:

  • Admins should have full access to all operations (Role-Based Access Control).
  • Users can view all blogs and manage (edit/delete) only their posts (Attribute-Based Access Control).
  • Read-only access is given to users for blog posts they did not create.

Breakdown of Privileges

Types of Policies

Resource Policies

Resource policies govern the actions permitted or forbidden on application entities known as resources. In a project management tool, for example, this could mean defining who can edit a particular task or who can view the entire project plan.

Derived Roles

Some identity providers (e.g., Okta, Auth0) also provide basic user roles. Derived roles enhance the IdP-provided roles by incorporating context-specific information for more granular access control. For instance, a general "team_lead" role could be converted to "team_lead_finance" by augmenting the role with logic considering the departmental context. This new role could then be granted specific access to the relevant finance department’s documents and not those of other departments.


Policy Creation with Cerbos

Cerbos Hub

Cerbos Hub is a centralized repository or platform designed to streamline the management and sharing of Cerbos policies. It acts as a collaborative space where developer, product, and security teams can access a variety of pre-built policy templates, develop their own custom policies, and utilize best practices for access control configuration. Cerbos Hub manages the validation, testing, compilation, and deployment of policy updates across all connected Cerbos policy decision points. As part of the continuous integration process, embedded bundles are also released, and clients can access the most recent versions through a specific URL.

Cerbos Policy Decision Point (PDP)

Cerbos PDP, or Policy Decision Point, is the core component of the Cerbos system that evaluates incoming access requests against defined policies to make authorization decisions. When an application queries the PDP with details about a user's action on a resource, the PDP assesses this request in the context of the applicable policies and returns a decision—allow or deny—based on the rules specified. The PDP ensures that access control logic is centralized, decoupling it from application code to enhance security, maintainability, and scalability.

Prerequisites for integrating Cerbos with Next.js

We suggest installing the dependencies with the following minimum versions to ensure compatibility with this tutorial. If your dependencies are not aligned with those we’ve mentioned, consider upgrading them now. 

  1. WSL (if Windows is your primary OS)
  2. Docker 
  3. Node.js version minimum requirement: 18

Getting started

First things first, we need to build the next.js application before implementing the authorization. We will continue with our blogging example.

File Structure of our Next.js Application

  • app/: Holds UI components and api routes, including pages and interfaces for our blog.
  • cerbos/: Dedicated to Cerbos authorization policies for access control.
  • data/: Contains blog post data or database connections.
  • lib/: Shared libraries or utility functions for application-wide use.
  • node_modules/: Stores external libraries.
  • public/: For static assets like images, CSS, and client-side JS.
  • utils/: Utility functions for specific tasks within the app.

To get started, clone this repository and follow along. Ensure you are in the root directory, in this case it’s /nextjs-cerbos.

Now run npm install to install dependencies.

To bootstrap or initialize the application, run the npm run dev in the root directory.

Once the build is successful, you should see the web page loading on your browser at localhost:3000.

Integrating Cerbos as a service

To integrate Cerbos in our Next.js application, we will begin by launching the Cerbos Docker container.

In the root directory (/nextjs-cerbos) use the following command to run the Docker container:

docker run --rm --name cerbos -d -v $(pwd)/cerbos/policies:/policies -p 3592:3592 -p 3593:3593 ghcr.io/cerbos/cerbos:0.34.0

Run docker ps to ensure the container persists.

Connecting Cerbos with the Next.js app

Next, we are using the @cerbos/grpc library to initialize the cerbos client. It is a client library for interacting with the Cerbos Policy Decision Point service over gRPC.

Generate Policy Files with Cerbos RBAC Policy Generator

A policy file dictates resource management and access in applications, outlining permissible actions based on user roles to ensure secure and flexible access control.

Now, we need to define the policies governing access control according to the needs of the application.

Write your RBAC (Role-Based Access Control) policy with a few clicks using Cerbos RBAC Policy Generator — it's easy to use, developer-friendly, and hassle-free to generate policies.

To create a policy file, we will include all our blogs as resources and checkboxes based on our application needs. After making our selections based on logic, we can click the Generate button to generate a YAML policy file named basicResource.yaml.

With the RBAC policy generator, in addition to generating the policy file, there is also an interface where you can select the principal and resource to check permissions based on the policy file.

  • Principal: Who is performing the action, in our case it could be user1, user3 etc.
  • Resource: What is being accessed, in our case it could be blog1, blog2 etc.

Should the user be User 1, the owner, or Users 2 or 4 with administrative privileges, access to all actions would be rightfully authorized.

Cerbos RBAC policy generator takes care of the syntax, while you can focus solely on the logical aspect of the policies.

The RBAC Policy Generator can be used to create drafts of your policies or even full-fledged policies if the logic incorporates the needs of your application. However, for this article we also  created custom policies for our next.js blogging application. 

We have depicted a basic user-based resource policy in our documentation. You can refer to it and modify it as per your business policies.

To effectively manage the authorization logic, we can organize the policies into separate files: one for outlining derived roles, another for specifying principal policies when a user requires special access, and a third for detailing the main policies required by the application. These are considered best practices. For guidance on writing these policies, you can refer to examples at play.cerbos.dev.

The policies directory under cerbos in the next.js folder structure (path: /nextjs-cerbos/cerbos/policies ) has YAML files that take care of the policy configurations: (blog_post_roles.yaml) determines the derived roles, and (blog_policy.yaml) defines the access control based on these roles.

blog_post_roles.yaml

This policy grants users a derived role of "owner" if their ID matches the owner attribute of a blog post, thereby simplifying the declaration of complex access control logic.

blog_policy.yaml

This policy document outlines access rules for a "blog" resource. It grants reading privileges to a role admin and a derived role owner, ensuring broad visibility. Admins are given full capabilities to modify or delete any blog post, signifying complete content oversight. For users, update and delete permissions are conditioned on ownership—users can only alter posts they own, preventing unauthorized modifications to others' content.

In this scenario, Cerbos functions as the Policy Decision Point (PDP), where it evaluates incoming requests against predefined policies to make authorization decisions. When a request to perform an action on a blog post is received, the Cerbos PDP initially identifies the relevant resource policy based on the type of resource involved. It then determines if there are any derived roles that need to be evaluated for the specific context of the request. 

Following this, Cerbos PDP assesses the applicable rules associated with the action requested. This process ensures that the decision to grant or deny access is made according to the established policies, considering the user's relationship to the resource, such as whether the user owns the post.

The cerbos.checkResource function evaluates if a user (the "principal") has permission to perform specified actions on a resource. It processes the user's ID, roles, and attributes, alongside the resource's type, ID, and attributes, against defined access policies. By listing actions like ["read," "edit," "delete"], the function returns a decision for each, ensuring actions align with user permissions. This mechanism centralizes and simplifies access control, maintaining application security by enforcing policy-based permissions efficiently.

Now that we've integrated Cerbos authorization and created the policies, now let's run our next.js app using npm run dev.

Now that the app is running, you can access it by navigating to localhost:3000. 

And test whether the app aligns with the defined policies.

Testing authorization in a Next.JS application

To test the efficiency and functionality of our access restriction features, we will attempt to alter a blog document or post without owning it or having administrative access. Our system should detect this unauthorized attempt and block access accordingly.

When user3 attempts to edit blog1, written by user1, the request should be denied, and an alert stating "unauthorized user" should be displayed.

Now let’s try to delete a blog post as the owner or administrator to evaluate the performance of our access control system from a different angle. In this case, our system identifies the user's permitted status and provides access to the desired modifications.

The above demonstration shows that the blog owner can delete their blog.

Let us take a deep-dive into what actually is happening under the hood:

  • A user (the principal) attempts to delete (action) a blog post (resource).
  • The Cerbos PDP receives the request and evaluates it based on the defined policy.
  • The PDP checks if the blogPost resource's owner attribute matches the currentUser's id.
  • If the condition is true, a derived role of “owner” is given to the user, and based on the role, action is either allowed or denied. The test successfully proved the viability of Cerbos' access control, enabling safe and efficient content and identity management for authorized users and consumers.

Frequently Asked Questions

Q1. How Can Attribute-Based Access Control (ABAC) Be Utilized in Next.js for Dynamic Authorization?

Answer: By creating granular access control policies that consider user attributes, resource attributes, and context, developers may implement ABAC by integrating Cerbos with Next.js. In particular, you can:

  • Send a request from your Next.js middleware or API routes to Cerbos' Policy Decision Point (PDP). This request contains the activity the user attempts to complete, the resources they are accessing, and their attributes (roles, departments, etc.).
  • When you compare this information to the policies you've specified in the policy language (YAML or JSON), Cerbos determines whether to provide access.
  • With this method, access control may be dynamic and fine-grained. Permissions can be immediately adjusted within your Next.js application flow based on any change in an attribute or context.

Q2. How do you implement access control for static and dynamic pages in Next.js?

Answer: Access control with Cerbos can be implemented for static pages in Next.js either at build time or on a per-request basis using getStaticProps or getServerSideProps, respectively. Use client-side fetching to examine Cerbos' PDP or incorporate Cerbos into your API routes for dynamic pages or client-side rendered components. Actions consist of:

  • Defining user roles and attributes in Cerbos access control policies that dictate which pages and resources must be accessed.
  • Utilize Next.js functions to query Cerbos with the user's current session information and the resource they want to view when a page is requested.
  • Cerbos will decide whether to render the page, send the user to an error or login page, or retrieve and dynamically display the content on the client side.

Q3. How Do You Implement Role-Based Access Control (RBAC) in Next.js Applications?

Answer: Access control is streamlined and secured when RBAC is implemented in Next.js with Cerbos by:

  • Centralizing Policies: By eliminating dispersed logic throughout the application, Cerbos centralizes access control policies.
  • Increasing Scalability: Cerbos' scalable approach eliminates the complexity of custom logic management by enabling simple role or permission updates as your application expands.
  • Cerbos ensures consistency by acting as a single source of truth for access policies. This is important because it allows apps to interact with shared services uniformly.
  • Dynamic Access Decisions: Cerbos offers dynamic policies considering user context, attributes, and resource specifics, providing flexible control that adjusts to changes. This is in contrast to static roles in conventional systems.
  • Cutting Complexity: Cerbos drastically cuts boilerplate code and complexity by abstracting access control into declarative policies. 

Conclusion

You have successfully integrated Cerbos authorization into a sample Next.js blogging application through the following key steps:

  • Deploying Cerbos in a Docker container to ensure a consistent and isolated environment.
  • Crafting and refining policy files to define clear access control rules.
  • Rebuilding the application to incorporate these policies.
  • Verified that the application adheres to the established authorization rules, ensuring secure and controlled access.

Through Cerbos policy files, you are now equipped to design sophisticated authorization rules that ensure only authorized users can access specific data. With Cerbos Hub, you can fully realize the potential of scalable, secure authorization. Sign up now to protect your apps and streamline access control.

GUIDE
INTEGRATION

Book a free Policy Workshop to discuss your requirements and get your first policy written by the Cerbos team