Nuxt authorization: How to implement fine-grained access control

Published by Aldin Kiselica on January 12, 2024
image

In this tutorial you will learn how to use Cerbos to add fine-grained access control to any Nuxt web application, simplifying authorization as a result.

Check out a working example of a Cerbos integration in a Nuxt application on GitHub.

Prerequisites

  • A working Nuxt web application
  • Docker installed in your working environment

Steps

Having met the prerequisites, the steps are as follows:

Set up the Cerbos policy repository

A Cerbos policy repository is a collection of YAML files that define access control rules for your application. It's essential for specifying who can do what within your app. These policies dictate the actions different user roles can perform on various resources. A resource is an application-specific concept that applies to anything that requires access rules. For example, a document is a resource that can be altered, viewed, deleted, etc. by different users depending on the access control rules. Read more about the best practices for structuring your policy repository in our documentation.

Your Cerbos policy repository can be stored on your disk, on git, or any S3-compatible storage system. To set up a Cerbos policy repository on the disk, within the Nuxt application, do the following:

  • In your project's root, create a `cerbos` folder, then a `policies` sub-folder within it.
  • Within the `policies` folder, create a YAML file which will contain the rules for performing permission checks in your Nuxt application. For example, create a `documents.yaml` resource policy file.
  • Populate the `documents.yaml` file with rules for actions that can be performed on a document, check the example below. Learn more in Cerbos resource policies documentation.
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: default
  resource: document
  rules:
    - actions: ['view', 'change', 'destroy']
      effect: EFFECT_ALLOW
      roles:
        - admin

    - actions: ['view', 'change']
      effect: EFFECT_ALLOW
      roles:
        - user
      condition:
        match:
          expr: request.resource.attr.author == request.principal.id

Now we’ve defined a simple resource policy file `documents.yaml` which allows admin users to `view`, `change`, and `destroy` any `document` resource within the system. Non-admin users are allowed to `view` and `change` the `document` resource if the condition is matched that the given user is also the document author.

Note that a separate policy file is expected for different types of resources and principals we want to create policies about. Our GitHub demo shows an example of a `contacts.yaml` resource policy file alongside the `documents.yaml` one.

Cerbos policy repository can be further enriched with defining context-aware roles, schemas, scopes, tests, and various other features explained in comprehensive Cerbos documentation.

Set up the Cerbos PDP

Cerbos PDP is the Policy Decision Point, a component that evaluates application requests against authZ policies to make access decisions; determining whether a user action on a resource is allowed.

In this demo, we’ll set a Cerbos PDP within your repository, as a separate module, using the previously created `cerbos` directory. To run Cerbos PDP locally, do the following:

  • Create a Cerbos PDP server configuration file within `cerbos` directory. You can name it `conf.yaml`.
  • For simplest configuration, within the `conf.yaml` file define the port exposed for listening to HTTP (and/or gRPC) requests and the location of the Cerbos policy repository. The simplest `conf.yaml` file would look like the one shown below:
server:
  httpListenAddr: ":3592"

storage:
  driver: "disk"
  disk:
    directory: /policies

For more robust configuration, make sure to check our Cerbos PDP configuration documentation.

Your file Cerbos-related file structure now might look like the following:

.
├── cerbos
│   └── policies
│       ├── documents.yaml
│       └── contacts.yaml
└── conf.yaml

Position your terminal within the `cerbos` directory. If you have Docker installed, you can simply run the following command to start Cerbos PDP locally in a Docker container.

docker run --rm --name cerbos -t \
  -v $(pwd)/policies:/policies \
  -p 3592:3592 \
  ghcr.io/cerbos/cerbos:latest server
  --config=conf.yaml 

Cerbos PDP runs as a separate process from your Nuxt application. Here, we ran it as a standalone service within a docker container, but this is not a requirement. Cerbos is designed to be adaptable to your infrastructure. So if you want to run Cerbos PDP as a service, a sidecar, or a lambda function, for instance, check the Cerbos deployment documentation to learn more about Cerbos deployment patterns.

Add the Cerbos client to your Nuxt application

Adding a Cerbos client is essential for simplifying the communication between your Nuxt application and the Cerbos PDP. The Client will allow you to make user permission checks from your Nuxt application in an elegant manner.

To add the Cerbos client to your Nuxt application, follow these steps:

  • Select the Cerbos client you want to use.
  • Choose between `@cerbos/http` for HTTP interactions or `@cerbos/grpc` for gRPC in server-side applications.
  • Install the chosen client package using the `npm install` command
  • In your Nuxt project, within the `utils` folder, create a utility file `cerbos.ts` to instantiate the Cerbos client.
  • Export the instance for reuse across your Nuxt application.

The `cerbos.ts` file would look like this:

import { HTTP as Cerbos} from "@cerbos/http";

export const cerbos = new Cerbos("localhost:3592");

Create an API route

A Nuxt API route is a feature in Nuxt.js used to define server-side logic and handle HTTP requests within the Nuxt application. These routes allow developers to create various back-end functionalities in the Nuxt project, such as data fetching, form submissions, or in this case authorization.

To create an API route in Nuxt for Cerbos permission checks:

  • Create an `isAllowed.ts` file within the `server/api` folder of your Nuxt application.
  • Define and export the event handler
  • In `isAllowed.ts` file, use `defineEventHandler` from `nuxt-helpers` to export your API handling function.
  • Within this function, call the Cerbos `.isAllowed(...)` method to check user permissions based on the request's user and resource details.
import { cerbos } from "../utils/cerbos";

// Pull the user info from the session in your Nuxt applications
const user = {
    id: "000012345",
    roles: ["user"]
  }

export default defineEventHandler(async (event) => {
    const query = getQuery(event)
    const document = {
        id: query.documentId as string
    }

    const requestBody = {
        principal: user,
        resource: { // Pull the resource info from the data storage in your Nuxt applications. Pass Resource ID within the event as a query param, like we did here.
          kind: "document",
          id: document.id,
          attributes: {
            author: "000012347"
          }
        },
        action: "approve"
      }    

    const isAllowed = await cerbos.isAllowed(requestBody)
    .catch( (error)=> {
      console.log( error );
      return false;
    })
})

That’s it! You can now universally call this API as `/api/isAllowed` across your Nuxt application pages and components.

Make permission checks across your Nuxt pages and components

Let’s see the usage of Cerbos permission checks across your Nuxt application pages in action. Let’s say we’ve created a page for each of our documents on the path `/documents/[id]`. To do that

  • Create  a `documents` folder under the `pages` folder of your application.
  • Within the `documents` folder, create an `[id].vue` file.

Nuxt offers route validation via the `validate` property in `definePageMeta()` in each page you wish to validate. The `validate` property accepts the route as an argument. 

To use the `/api/isAllowed` route defined in the previous step you can add the following snippet to the bottom of your recently created `[id].vue` (or any other vue) file.

definePageMeta({
  validate: async (route) => {
    const { data: isAllowed } = await useFetch('/api/isAllowed', 
    {
      headers: useRequestHeaders(),
      query: { documentId: route.params.id }
    })
    return isAllowed.value;
  }
})

It’s that simple!

To summarize

All you have to do to use Cerbos authorization in your Nuxt application is to:

  • Create Cerbos policies you will be using to make decisions over who should be able to do what within your application,
  • Run a Cerbos PDP which upon being called will be checking those policies,
  • Install the Cerbos client within your Nuxt application,
  • Create an API route to enable its usage across your Nuxt application,
  • Start invoking the exported API route to perform Cerbos `.isAllowed(...)` checks and determine whether your application user should be allowed to make a certain action on a certain resource.

Further steps

In this tutorial you saw how to add fine-grained access control with Cerbos to your Nuxt applications. If you liked it make sure to check out Cerbos Hub.

Cerbos Hub saves time managing and synchronizing every Cerbos PDP and policy repository across all your applications and services. It helps connect your Cerbos policy repository and your Cerbos PDP instances and manage them collaboratively and smoothly from a single place.

We also published a series of articles with step-by-step instructions on how to Get started with Cerbos Hub. Take a look if you’re interested in seeing how it works!

DOCUMENTATION
GUIDE
INTEGRATION

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