System security is one of the key considerations when building software, and there are various mechanisms used in ensuring a software system is secure. The common ones are role-based access control (RBAC) and attribute-based access control (ABAC).
AccessControl, a Node.js module, can be used to implement these two access control mechanisms. Before diving into how AccessControl works, let’s briefly explain these two mechanisms and how they work.
Role-based access control, also known as role-based security, is a mechanism that restricts system access to users using their roles and privileges and permissions.
Within an application, roles are created for various user types (e.g., writer or reader). The permission to perform certain actions or access application resources are assigned to specific roles. For instance, in a writing application, a writer can be granted the permission to create, update, read, and delete a post, whereas a reader can be restricted to only being able to read a post.
When using RBAC, there are three guiding rules:
A user can have multiple roles and a role can have multiple permissions. RBAC also supports role hierarchy where a high-level role inherits the permissions of its sub roles. Additional constraints may be applied to place restrictive rule on the potential inheritance of permissions from another role. Some examples of constraints are:
Attribute-based access control, also known as policy-based access control for IAM, defines an access control paradigm whereby access rights are granted to users through the use of policies which combine attributes together. The policies can use any type of attributes, such as user attributes, resource attributes, object, and environment attributes.
ABAC can be used to complement RBAC in that in addition to roles and permissions, a policy can be used to define what attribute is allowed or not allowed.
AccessControl
, a Node.js module, merges the best features of RBAC and ABAC. It implements RBAC basics and also focuses on resource and action attributes. For a full list of the module’s features, view the documentation.
With npm: npm i accesscontrol --save
.
With Yarn: yarn add accesscontrol
Roles serve as containers for permissions. They are assigned to users depending on their responsibility. You can create and define roles simply by calling .grant(<role>)
or .deny(<role>)
methods on an AccessControl
instance.
import { AccessControl } from 'accesscontrol'; const ac = new AccessControl(); ac.grant('reader');
Roles can extend other roles. You can extend a role by calling .extend
on an existing role.
ac.grant('reader').extend('writer');
Actions and action-attributes represent what can be performed on resources by role(s). They are a finite fixed list based on classic CRUD. There are two action-attributes which define the possession of the resource by a role: own and any.
For example, an editor
role can create
, read
, update
or delete
(CRUD) any post
resource. But a writer
role might only read or update its own post
resource.
You can define an action and possession on a resource using: createOwn
, readOwn
, updateOwn
, deleteOwn
, createAny
, readAny
, updateAny
, and deleteAny
methods.
const ac = new AccessControl(); ac.grant('reader') .readAny('post') .grant('writer') .createOwn('post') .deleteOwn('post') .readAny('post') .grant('editor') .extend('writer') .updateAny('post') .deleteAny('post');
These represent system elements that we want to protect, such as post
. Multiple roles can have access to a specific resource but may not have equal access to all attributes of the resource. You can use Glob notation to define allowed or denied attributes.
For example, we have a post
resource that has the following attributes: id
, title
, and description
. All attributes of any post
resource can be read by an editor
role:
ac.grant('editor').readAny('post', ['*']);
But the id
attribute should not be read by a reader
role.
ac.grant('reader').readAny('post', ['!id']);
The permission granted is determined using a combination of role, action, and resource. You can add .can(<role>).<action>(<resource>)
on an AccessControl
instance to check for granted permissions for a specific resource and action.
const permission = ac.can('reader').readAny('post'); permission.granted; permission.attributes; permission.filter(data);
You can pass the grants directly to the AccessControl
constructor. It accepts either an Object
:
let grantObjects = { reader: { post: { 'read:any': ['*', '!id] } }, writer: { post: { 'create:own': ['*'], 'read:any': ['*'], 'update:own': ['*'], 'delete:own': ['*'] } }, editor: { post: { 'create:any': ['*'], 'read:any': ['*'], 'update:any': ['*'], 'delete:any': ['*'] } } } const ac = new AccessControl(grantsObject);
Or an array:
let grantArray = [ { role: 'reader', resource: 'post', action: 'read:any', attributes: '*, !id' }, { role: 'writer', resource: 'post', action: 'read:any', attributes: '*' }, { role: 'writer', resource: 'post', action: 'create:own', attributes: '*' }, { role: 'writer', resource: 'post', action: 'update:own', attributes: '*' }, { role: 'writer', resource: 'post', action: 'delete:own', attributes: '*' }, { role: 'editor', resource: 'post', action: 'read:any', attributes: '*' }, { role: 'editor', resource: 'post', action: 'create:any', attributes: '*' }, { role: 'editor', resource: 'post', action: 'update:any', attributes: '*' }, { role: 'editor', resource: 'post', action: 'delete:any', attributes: '*' }, ] const ac = new AccessControl(grantArray);
const ac = new AccessControl(grants); router.get('/posts/:title', function (req, res, next) { const permission = ac.can(req.user.role).readAny('post'); if (permission.granted) { Video.find(req.params.title, function (err, data) { if (err || !data) return res.status(404).end(); res.json(permission.filter(data)); }); } else { res.status(403).end(); } });
We have shown how we can use AccessControl
for authorization in a server-side application. We can also use it to authorize routes and UI elements in a client-side application, which can be done by using the same grant object for both the server and the client. AccessControl
is one only library for implementing access control in Node.js. Node-casbin and CASL are also Node.js libraries for implementing access control.
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
2 Replies to "How to use AccessControl for RBAC and ABAC in Node.js"
Great post & thank you for sharing, one of the good blogs to read abou tAccessControl for RBAC
I’m glad I found this, thanks Godwin!
Few comments/questions:
– AccessControl was last published in February 2018. Is it still active?
– I was confused until I realized that “post” was a resource and not a route or http method. It would be less confusing if the example resource was “article” or “video.”
– At the beginning it states “A user can have multiple roles” but the example with Express contains “ac.can(req.user.role).readAny(‘post’)”. Can req.user.role be an array of roles?
– There is a JSON typo: ‘read:any’: [‘*’, ‘!id]