Cerbos is a Simple Addition for Authorization in Your Next.js Application

Published by Bruce Wiggleston on August 18, 2022
image

Next.js supports multiple authentication patterns, each designed for different use cases. But once the software knows who the user is, authorization becomes the most important factor. You have to determine this user’s permissions within the software. You can go through and write out the code, trying to think of every possible avenue you need your authenticated users to access, or you can use Cerbos.

Cerbos is a solution for handling all the use cases for your authenticated users and what they are able to access and change within your application. Everything with Cerbos is straightforward and easy to follow, allowing you to focus on other aspects of your application without going through the frustrations that come along with dealing with authorization on your own.

Next.js

Let’s start by talking about what Next.js is, and why you would want to use it for your next application. It is an open-source framework that lets you build server-side rendering and static web applications using React.

Next.js also gives you a great experience with all the features like TypeScript support, smart bundling, route prefetching, and more. There is no need to configure anything to get your application running as it comes with its own configuration. There is no need for webpack, or something like it, to start using Next.js.

All you have to do after getting the initial install and app created is run npm run dev, and you are ready to go.

Authentication in Next.js

To understand authentication in Next.js, you need to first decide what you want to do. There are two main patterns of authentication in Next.js that require you to decide what you want to do.

You can use static generation to server-render a loading state which is then followed by fetching user data client-side, or you can fetch user data server-side to eliminate a flash of unauthenticated content.

There are a multitude of ways to plug authentication into your application with Next.js, and below are just a couple of examples on setting up a simple user profile and login page.

Here is an example of a user sign in using a statically generated page:

// pages/user.js

import useUser from '../lib/useUser'
import Layout from '../components/Layout'

const UserProfilePage = () => {
  // Fetch the user client-side
  const { user } = useUser({ redirectTo: '/login' })

  // Server-render loading state
  if (!user || user.isLoggedIn === false) {
    return <Layout>Loading...</Layout>
  }

  // Once the user request finishes, show the user
  return (
    <Layout>
      <h1>Your Profile</h1>
      <pre>{JSON.stringify(user, null, 2)}</pre>
    </Layout>
  )
}

export default UserProfilePage

Or you could use another service like Clerk to simplify this for the profile:

import { UserProfile } from '@clerk/nextjs'

const UserProfilePage = () => <UserProfile path="/user" routing="path" />

export default UserProfilePage

And you would add this for the signup page:

import { SignUp } from '@clerk/nextjs'

const SignUpPage = () => (
	<SignUp path="/sign-up" routh="path" signInUrl="/sign-in" />
)

export default SignUpPage

Whatever option you choose is up to you, and that is part of what makes Next.js an effective and simple-to-use aspect of authentication for your application.

Adding Cerbos for Authorization

When it comes to adding authorization, nothing is as simple as adding Cerbos. It allows for independent authorization, so there is no extra bloat from token requests. You don’t have to worry about additional workarounds either.

It also allows you to give fine grained access controls that extend the roles defined in NextJS while also giving more contextual access controls. In this way, Cerbos is a great addition for authorization.

Adding it to your application by defining access policies using human-readable YAML is a breeze. There is no need to learn a new policy language.

Start by creating a directory to hold the config file and policies:

mkdir -p cerbos-quickstart/policies

One YAML file will be labeled config.yaml. For configuration, it would look like this:

---
server:
 httpListenAddr: ":3592"
 grpcListenAddr: ":3593"

storage:
 driver: "disk" # Valid values are "disk" or "git"
 disk: # Only required if "driver" is "disk"
   directory: /policies
   watchForChanges: true

The other YAML file will be labeled contact.yaml. This sets what your users will be able to do:

---
apiVersion: api.cerbos.dev/v1
resourcePolicy:
 version: default
 resource: contact
 rules:
   - actions: ["read", "create"]
     effect: EFFECT_ALLOW
     roles:
       - admin
       - user

   - actions: ["update", "delete"]
     effect: EFFECT_ALLOW
     roles:
       - admin

   - actions: ["update", "delete"]
     effect: EFFECT_ALLOW
     roles:
       - user
     condition:
       match:
         expr: request.resource.attr.owner == request.principal.id

After you have set up your config and policies, you will navigate and set up your API folder to add Cerbos to your application. This is what enables access decisions in milliseconds.

First, you need to install the client library for interacting with the Cerbos policy decision point over gRPC from server-side Node.js applications, and it would like this:

$ npm install @cerbos/grpc

or

$ yarn add @cerbos/grpc

For this API example, we have named our file getResources.js, and the code within in it would look like this:

import { requireSession, users } from "@clerk/nextjs/api";
import { GRPC as Cerbos } from "@cerbos/grpc";
const cerbos = new Cerbos(
 "demo-express-clerk-cerbos-pdp-qh5dbmiiva-uk.a.run.app",
 {
   tls: true,
 }
);

export default requireSession(async (req, res) => {
 const user = await users.getUser(req.session.userId);

 const roles = user.publicMetadata.role
   ? [user.publicMetadata.role]
   : ["user"];

 const cerbosPayload = {
   principal: {
     id: req.session.userId,
     roles, //roles from Clerk profile
     attributes: {
       email: user.email,
     },
   },
   resources: [
     {
       resource: {
         kind: "contact",
         id: "1",
         attributes: {
           owner: req.session.userId,
           lastUpdated: new Date(2020, 10, 10),
         },
       },
       actions: ["read", "create", "update", "delete"],
     },

     {
       resource: {
         kind: "contact",
         id: "2",
         attributes: {
           owner: "test2",
           lastUpdated: new Date(2020, 10, 10),
         },
       },
       actions: ["read", "create", "update", "delete"],
     },
   ],
 };
 console.log(cerbosPayload);

 const result = await cerbos.checkResources(cerbosPayload);
 res.json(result.results);
});

As you can see, setting up authorization doesn’t have to be a large endeavor, and shouldn’t take a team long to implement into an application, saving time and money on a project.

Conclusion

Next.js is a quickly growing framework used with React, and Cerbos is an open-source, decoupled access control for your application. With any new application that you plan on making, it should be secure. Authentication allows users to be secure with login information and who they are. Authorization is an important part of any secure application by giving users roles and permissions within the application.

We have looked at why Next.js may be the framework you choose to use for your next application, and we also looked at an example of how it handles authentication.

Cerbos comes in to make authorizing users a simple task that can save time and money during application development. Cerbos makes it simple to define access policies using human-readable YAML because there’s no need to learn a new policy language, and accessing the API takes milliseconds.

Integrating Cerbos with NextJS should be the next thing you should try in your application for powerful authorization!

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