Full guide to Next.js authorization & authentication

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.

Benefits of integrating Cerbos with NextJS

Integrating Cerbos with NextJS will provide a variety of benefits related to security and authorization, including:

Fine grained authorization control

By integrating Cerbos with NextJS you will have the ability to define access control policies for your NextJS applications, specifying which user can perform which actions when and in what context.

Dynamic NextJS authorization

When you integrate Cerbos with NextJS you can make real-time decisions regarding access to your NextJS applications based on user attributes and other factors.

Enhanced security

Integrating Cerbos authorization capabilities into your NextJS applications means you can introduce an extra layer of security that ensures only specifically designated users gain access to certain sensitive types of information and that only people with a compelling need to do so are able to perform specific actions.

More reliable compliance

Integrating Cerbos with your NextJS application can help you improve compliance with various regulatory requirements. You can also produce audit logs of any and all authorization decisions.

Cleaner code

When you separate authorization logic from the rest of your application code that code becomes cleaner, easier to maintain and easier to troubleshoot. With Cerbos handling authorization there are no complicated conditional checks mucking up your application code.

Fewer cross-cutting concerns

Cross-cutting concerns are those aspects of a given application that affect multiple components including security and logging. Cross-cutting can make application code messy and difficult to test. NextJS authorization is often a cross cutting concern as well, but by handing off authorization to Cerbos you can reduce authorization-related cross-cutting concerns.

Scalability

Cerbos will easily scale with any of your NextJS applications enabling you to continue to enforce authorization mandates as your application scales up to meet your needs.

Ample community support

Like NextJS, Cerbos has an active community ready to provide you with timely and valuable resources and support as you work your way through the integration process. You will also find extensive and detailed integration instructions in the Cerbos documentation.

Integrating Cerbos with NextJS RBAC typically involves configuring your NextJS application to make NextJS authorization requests to Cerbos. Then configuring the app to process the responses from Cerbos in the desired manner.

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. Finally, we explored some of the benefits of integrating Cerbos with NextJS.

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!

GUIDE

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