Authorization

Novel uses the CASL library to provide authorization checks for all the routes and actions you need for your SaaS.

Important

It is important to remember that authorization is an intentional exercise and not automatic. You need to put these checks in place where they are needed.

Configuration

In /config/auth.js there is a roles object that you can modify to add role IDs and permission keys to.

config/auth.js
/**
 * These are roles within the app that can be assigned to users.
 *
 * They have permissions that can be applied per route. Additional permission checks can be added
 * per transaction via casl/ability.
 *
 * Make sure you update app/(app)/organization/team/members.tsx if you modify this.
 */
roles: {
	superuser: ['all', 'admin'],
	admin: ['read', 'write', 'delete'],
	user: ['read', 'write'],
},

You can add as many roles you need here, and as many permissions applied to the role here.

These are then auto-loaded during start up time and applied to a global authorizer and a session based authorizer.

Permission

The Permission object is a class that you can use to organize permissions in your application.

feature.ts
const permit = new Permission(anyActorWithAnId);

permit.allow('read', Post);
permit.deny('delete', Post);

await permit.persist();

// or fetch a stored permission
const permit = await Permission.fetch(anActorWithAnId);

new Permission(ObjectWithId)

This creates a new Permission object that can be serialized later on. You can use this to temporarily create permissions in your feature code.

You can persist the permissions to database using .persist() below.

permit.allow(action: String, subject: String | ObjectWithId)

This defines what you can use in the Permission object using the CASL Ability .can or .cannot. The signature for this is the same.

permit.deny(action: String, subject: String | ObjectWithId)

This is the opposite of allow you can use in the Permission object using the CASL Ability .can or .cannot. The signature for this is the same.

permit.build(): Ability

This is an internal abstraction to CASL's Ability.build function. This makes it so that you can perform CASL functions against the Permission Object

feature.ts
const permit = new Permission(anyActorWithAnId);

permit.allow('read', Post);
permit.deny('delete', Post);

const permissions = permit.build();

permission.can('read', Post); // returns true

await permit.persist()

This persists the permissions defined during the lifecycle of the Permission object. It generates a serialized permission records, one row per action/scope into the access_control table.

await Permission.fetch(actor: String | ObjectWithId)

This is similar to new Permission(ObjectWithId) but for an actor that has been persisted.

feature.ts
const permissions = await Permission.fetch(anyActorWithAnId);

permission.can('read', Post); // returns true

Using with an API

It is important to be familiar with both authorization modes of Novel API. Discussed below:

Combine the above with the request helpers associated with permit.allowand permit.deny.

Changelog

  • 2024-12-20 - Initial Documentation

Last updated

Was this helpful?