A bear playing hopscotch

Implementing Attribute-based Access Control (ABAC) in Node.js With Oso

Valeri Karpov

Attribute-based Access Control (ABAC for short) is a pattern for determining whether a user is authorized to perform a certain action based on attributes associated with the user or the resource the user is trying to access. With ABAC, you can define complex rule sets that can take into account any property of the user or the resource. Role-based access control (RBAC) is often easier to work with than ABAC, but, in certain cases, ABAC offers you more flexibility than RBAC.

For example, consider a message board application: users can create threads and comment on threads, and admins can prevent any further comments on threads by locking a thread. RBAC doesn't have the ability to represent the concept that users cannot comment on locked threads, because whether or not a thread is locked is an attribute of the thread. But ABAC can take into account whether or not the thread is locked.

In this blog post, I'll show how you can implement ABAC with Oso Cloud for a Node.js message board app. The app will support locking threads and banning users, two common authorization tasks that ABAC is perfectly suited for.

Introducing the Thread API

The message board app has two primary objects: users and threads. You can find the full source for this message board app on GitHub. There are Express endpoints for creating a new user, creating a thread, and retrieving a list of all threads as follows.

To simplify the implementation, the app stores users and threads in memory, and the authorization header just contains the acting user's id. In order to comment on the thread with id thread1 as user user1, you would send the following HTTP request to the Thread API:

Adding ABAC With Oso

Without authorization, any user can comment on any thread, ban any user, or lock any thread. To make this API more secure, the API needs the following rules:

  1. Only admins can lock threads (RBAC)
  2. Only admins can ban users (RBAC)
  3. Only admins can comment on locked threads (ABAC)
  4. Banned users cannot comment on threads (ABAC)

Oso Cloud makes it easy to implement this sort of authorization in Node.js. In Oso Cloud, facts are how you represent attributes. Whether a thread is locked or whether a user is banned is represented as a fact. For example, the below screenshot contains some example facts for the thread API:

User admin1 is an admin, thread thread1 is not locked, and user user1 is banned. admin1 can ban users or lock threads, and user1 can't comment on threads.

The following is how you can represent the above constraints using Oso's Polar syntax in Oso Cloud. There are two objects: Threads and Users. The “moderate” permission on a Thread grants a user access to lock or unlock a thread, and the “comment” permission grants a user access to comment on a thread. Similarly, the “ban” permission on a User grants a user access to ban another user. Admins can moderate and ban. Any user can comment unless the thread is locked or they are banned.

You can try out these rules in the Oso Cloud UI's "Explain" tab. For example, the following screenshot shows that Oso Cloud correctly infers that user1 can't comment on thread1 because they are banned.

Integrating Oso Cloud with Node.js

Implementing the authorization checks with the oso-cloud npm package is straightforward. First, import the Oso class and instantiate a new Oso Cloud client using your Oso Cloud API key as follows.

To check whether a given user is authorized to comment on a given thread based on the authorization header, you can use the oso.authorize() function. oso.authorize()  takes the actor, the action, and the resource as parameters; and returns a truthy value if the user is authorized to perform the given action. The following PUT /threads/:id/comment endpoint uses oso.authorize() to check whether the given user has the “comment” permission.

When you create a new thread, you need to tell Oso Cloud that the thread is not locked. And, if an admin bans a user or locks a thread, you need to update the corresponding facts in Oso Cloud as well. For example, the following POST /users endpoint shows how you can tell Oso Cloud whether a user is an admin using the oso.tell() function.

The following example shows how you can update whether a thread is locked in Oso Cloud using the oso.bulk() function. bulk() takes two parameters: a list of facts to delete, and a list of facts to create. The bulk() function atomically deletes existing facts and adds new ones, which enables you to implement semantics similar to an UPSERT and avoid conflicting facts.

The following example shows the endpoint for banning a user. Like the endpoint for locking a thread, this endpoint first needs to check if the user is authorized to ban another user. Then, the endpoint needs to update the is_banned fact for the given user in Oso Cloud using oso.bulk().

Moving On

ABAC is a powerful and flexible authorization pattern based on arbitrary properties of the user and the resource the user is accessing. In Oso Cloud, facts are how you can associate properties with users and resources, and Oso Cloud enables you to express custom rules that use facts to determine whether a user can perform a given action. As a Node.js developer, you just need to make sure your app updates facts in Oso Cloud correctly, and Oso Cloud handles determining whether a user has a certain permission in a more maintainable way than one-off if statements in your application logic.

Ready to level up your app's authorization setup? Check out Oso Cloud today

Want us to remind you?
We'll email you before the event with a friendly reminder.

Write your first policy