Convert Figma logo to code with AI

nestjsx logonest-access-control

Role and Attribute based Access Control for Nestjs 🔐

1,115
79
1,115
30

Top Related Projects

An authorization library that supports access control models like ACL, RBAC, ABAC in Node.js and Browser

Role and Attribute based Access Control for Node.js

Access control lists for node applications

JsonWebToken implementation for node.js http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html

1,697

An unopinionated authentication library for building Node.js APIs.

Quick Overview

nestjsx/nest-access-control is a role-based access control (RBAC) module for NestJS applications. It provides a flexible and powerful way to manage permissions and roles in your NestJS projects, allowing you to define granular access rules for different resources and actions.

Pros

  • Easy integration with NestJS framework
  • Flexible and granular permission management
  • Supports both role-based and attribute-based access control
  • Lightweight and performant

Cons

  • Requires careful planning of roles and permissions structure
  • May add complexity to smaller projects
  • Limited documentation for advanced use cases
  • Potential learning curve for developers new to RBAC concepts

Code Examples

  1. Defining roles and permissions:
import { RolesBuilder } from 'nest-access-control';

export const roles: RolesBuilder = new RolesBuilder();

roles
  .grant('user')
    .readAny('profile')
    .updateOwn('profile')
  .grant('admin')
    .extend('user')
    .createAny('profile')
    .updateAny('profile')
    .deleteAny('profile');
  1. Using the @UseRoles() decorator:
import { UseRoles } from 'nest-access-control';

@Controller('profiles')
export class ProfilesController {
  @Get()
  @UseRoles({
    resource: 'profile',
    action: 'read',
    possession: 'any',
  })
  findAll() {
    // ...
  }
}
  1. Checking permissions in a service:
import { Injectable } from '@nestjs/common';
import { InjectRolesBuilder, RolesBuilder } from 'nest-access-control';

@Injectable()
export class ProfileService {
  constructor(@InjectRolesBuilder() private readonly rolesBuilder: RolesBuilder) {}

  async updateProfile(userId: string, profileData: any, currentUser: any) {
    const permission = this.rolesBuilder.can(currentUser.role).updateOwn('profile');
    
    if (permission.granted && userId === currentUser.id) {
      // Perform update
    } else {
      throw new ForbiddenException('You are not allowed to update this profile');
    }
  }
}

Getting Started

  1. Install the package:
npm install nest-access-control
  1. Import the AccessControlModule in your app.module.ts:
import { AccessControlModule } from 'nest-access-control';
import { roles } from './roles';

@Module({
  imports: [
    AccessControlModule.forRoles(roles),
    // ...
  ],
})
export class AppModule {}
  1. Use the @UseRoles() decorator in your controllers and inject the RolesBuilder in your services to start implementing access control in your application.

Competitor Comparisons

An authorization library that supports access control models like ACL, RBAC, ABAC in Node.js and Browser

Pros of node-casbin

  • More flexible and powerful policy model with support for RBAC, ABAC, and custom policies
  • Language-agnostic, can be used with various programming languages and frameworks
  • Extensive documentation and examples for different use cases

Cons of node-casbin

  • Steeper learning curve due to its more complex policy model
  • Requires more setup and configuration compared to nest-access-control

Code Comparison

nest-access-control:

@UseGuards(ACGuard)
@UseRoles({
  resource: 'video',
  action: 'create',
  possession: 'any',
})
@Post()
create(@Body() createVideoDto: CreateVideoDto) {
  // Implementation
}

node-casbin:

const enforcer = await newEnforcer('model.conf', 'policy.csv');
const sub = 'alice';
const obj = 'data1';
const act = 'read';

if (await enforcer.enforce(sub, obj, act)) {
  // Alice is allowed to read data1
} else {
  // Denied
}

Both libraries provide access control functionality, but node-casbin offers more flexibility and power at the cost of increased complexity. nest-access-control is more straightforward and integrates seamlessly with NestJS, while node-casbin can be used across various frameworks and languages.

Role and Attribute based Access Control for Node.js

Pros of accesscontrol

  • Framework-agnostic, can be used with any JavaScript/TypeScript project
  • More extensive documentation and examples
  • Supports attribute-based access control (ABAC) in addition to role-based access control (RBAC)

Cons of accesscontrol

  • Requires more manual setup and integration with NestJS
  • Less tailored to NestJS-specific features and decorators
  • May have a steeper learning curve for NestJS developers

Code Comparison

nest-access-control:

@UseGuards(ACGuard)
@UseRoles({
  resource: 'article',
  action: 'update',
  possession: 'own'
})
@Put(':id')
updateArticle(@Param('id') id: string) {
  // Update logic here
}

accesscontrol:

const ac = new AccessControl(grants);
const permission = ac.can('user').updateOwn('article');
if (permission.granted) {
  // Update logic here
}

Both libraries provide role-based access control, but nest-access-control offers tighter integration with NestJS through decorators and guards. accesscontrol is more flexible and can be used in various JavaScript environments, but requires more manual setup in a NestJS context. The choice between them depends on the specific project requirements and the developer's preference for either a more NestJS-tailored solution or a more general-purpose access control library.

Access control lists for node applications

Pros of node_acl

  • More mature and widely adopted project with a larger community
  • Supports multiple backends (memory, redis, mongodb) for storing ACL rules
  • Provides a simpler API for basic ACL operations

Cons of node_acl

  • Not specifically designed for NestJS, requiring additional setup and integration
  • Lacks built-in decorators and guards for easy use in NestJS controllers
  • May require more manual configuration for complex authorization scenarios

Code Comparison

node_acl:

acl.allow('user', 'blogs', 'view')
acl.isAllowed('joed', 'blogs', 'view', function(err, res){
  if(res){
    console.log("User joed is allowed to view blogs");
  }
});

nest-access-control:

@UseGuards(ACGuard)
@UseRoles({
  resource: 'blogs',
  action: 'read',
  possession: 'any'
})
@Get()
findAll() {
  return this.blogsService.findAll();
}

Both libraries provide ACL functionality, but nest-access-control offers tighter integration with NestJS through decorators and guards. node_acl provides a more flexible backend storage system and may be easier to implement in non-NestJS projects. The choice between them depends on the specific project requirements and framework being used.

JsonWebToken implementation for node.js http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html

Pros of node-jsonwebtoken

  • Widely adopted and well-maintained library for JWT handling
  • Simple and straightforward API for token generation and verification
  • Supports various algorithms and customization options

Cons of node-jsonwebtoken

  • Focuses solely on JWT operations, lacking built-in access control features
  • Requires additional implementation for role-based access control (RBAC)
  • No direct integration with NestJS framework

Code Comparison

node-jsonwebtoken:

const jwt = require('jsonwebtoken');
const token = jwt.sign({ userId: 123 }, 'secret', { expiresIn: '1h' });
const decoded = jwt.verify(token, 'secret');

nest-access-control:

@UseRoles({
  resource: 'article',
  action: 'read',
  possession: 'any',
})
@Get('articles')
getArticles() {
  // ...
}

Summary

node-jsonwebtoken is a popular library for JWT operations, offering simplicity and flexibility. However, it lacks built-in access control features. nest-access-control, on the other hand, provides a more comprehensive solution for role-based access control in NestJS applications, integrating seamlessly with the framework's decorators and modules. While node-jsonwebtoken excels in JWT handling, nest-access-control offers a higher-level abstraction for managing permissions and roles within a NestJS ecosystem.

1,697

An unopinionated authentication library for building Node.js APIs.

Pros of Permit

  • Lightweight and flexible, with a simple API for defining and checking permissions
  • Framework-agnostic, can be used with any JavaScript/TypeScript project
  • Supports both role-based and attribute-based access control

Cons of Permit

  • Less opinionated structure compared to Nest Access Control
  • May require more manual setup and configuration for complex scenarios
  • Lacks built-in integration with NestJS framework

Code Comparison

Permit:

const permit = new Permit({ ... });
permit.check(user, 'read', 'posts');

Nest Access Control:

@UseGuards(ACGuard)
@UseRoles({
  resource: 'posts',
  action: 'read',
  possession: 'any'
})

Key Differences

  • Nest Access Control is specifically designed for NestJS, offering tighter integration with the framework
  • Permit provides a more generic solution that can be adapted to various JavaScript environments
  • Nest Access Control uses a declarative approach with decorators, while Permit relies on programmatic checks

Use Cases

  • Choose Nest Access Control for NestJS projects requiring a structured, decorator-based access control system
  • Opt for Permit when working on non-NestJS projects or when a more flexible, lightweight solution is needed

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Nest Access Control

MIT npm version Open Source Love PRs Welcome forthebadge forthebadge

A helper Module for building a Role and Attribute based Access Control System for Nestjs

TL;DR: recently our system was needing to have a Control Panel, so you can control, and monitor every thing from there, and it was really really needing some Role based access control system, so i build this module for that, it is really cool, so i'd love to share it with you, and any PR are more than welcome :heart:

This module is built on top of onury's accesscontrol library here is some of it's Core Features

  • Chainable, friendly API.
    e.g. ac.can(role).create(resource)
  • Role hierarchical inheritance.
  • Define grants at once (e.g. from database result) or one by one.
  • Grant/deny permissions by attributes defined by glob notation (with nested object support).
  • Ability to filter data (model) instance by allowed attributes.
  • Ability to control access on own or any resources.
  • Ability to lock underlying grants model.
  • No silent errors.
  • Fast. (Grants are stored in memory, no database queries.)
  • Brutally tested.
  • TypeScript support.

What does this Module Provide?

In this module you will have all these features out of the box, but in nest-ish way.

  • It's Decorator-based so most of the time you will use decorators in your routes.
  • Built-in ACGuard so you can go and use it directly.
  • Access to the underlying AccessControl object from everywhere.

Installation

  • NPM:
npm install nest-access-control --save
  • Yarn:
yarn add nest-access-control

Example

See example folder for the more code

We need to build a Video service so users can share there videos with others, but we need some admins to control these videos.

  1. Let's first define our roles:

    To build our roles we will need the RolesBuilder class, it extends the AccessControl class from accesscontrol package.

    // app.roles.ts
    
    export enum AppRoles {
      USER_CREATE_ANY_VIDEO = 'USER_CREATE_ANY_VIDEO',
      ADMIN_UPDATE_OWN_VIDEO = 'ADMIN_UPDATE_OWN_VIDEO',
    }
    
    export const roles: RolesBuilder = new RolesBuilder();
    
    roles
      .grant(AppRoles.USER_CREATE_ANY_VIDEO) // define new or modify existing role. also takes an array.
        .createOwn('video') // equivalent to .createOwn('video', ['*'])
        .deleteOwn('video')
        .readAny('video')
      .grant(AppRoles.ADMIN_UPDATE_OWN_VIDEO) // switch to another role without breaking the chain
        .extend(AppRoles.USER_CREATE_ANY_VIDEO) // inherit role capabilities. also takes an array
        .updateAny('video', ['title']) // explicitly defined attributes
        .deleteAny('video');
    

Pro Tip :+1: : Keep all roles organized and in one file e,g: app.roles.ts

  1. Next let's use AccessControlModule in our Root module:
    // app.module.ts

    import { roles } from './app.roles';

    @Module({
      imports: [AccessControlModule.forRoles(roles)],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}

Until now everything is fine, but let's make our application, assume that we have list of video names, user can - according to our roles - create:own new video, and read:any video, so let's build it:

    // app.controller.ts
    ...
    @Controller()
    export class AppController  {
      constructor(private readonly appService: AppService)  {}
      @UseGuards(AuthGuard, ACGuard)
      @UseRoles({
        resource:  'video',
        action:  'read',
        possession:  'any',
      })
      @Get()
      root(@UserRoles() userRoles: any)  {
        return this.appService.root(userRoles);
      }
    }

ForRootAsync

Injecting providers for a RoleBuilder Factory (using a database to populate roles)

@Injectable()
class RoleProvider {

  getRoles(): Promise<string[]> {
    return Promise.resolve([
      'my-custom-role',
    ]);
  }
}

@Module({
  providers: [RoleProvider],
  exports: [RoleProvider],
})
class RoleModule {

}

@Module({
  imports: [
    AccessControlModule.forRootAsync({
      imports: [TestModule],
      inject: [RoleService],
      useFactory: async (roleService: RoleService): Promise<RolesBuilder> => {
        return new RolesBuilder(await roleService.getRoles());
      },
    }),
  ],
})
export class AccessModule {}

Notice the use of imports in the forRootAsync method. This will allow you to inject exported providers from the imported module. Injecting providers, provided in the same module as the imported AccessControlModule will result in the provider not being found. This is because the module is created before the providers.

So let's discuss what's going on!

First we introduced two new decorators, actually they are three, but let's see what they can do:

  • @UseRoles({ ... }): this the most used decorator, it define what roles should user have to access this route. It may take one or more role, but keep in mind that all roles must be satisfied. The structure of the role is really simple, for example, here we define what resources we have, and the ACGuard* - Damn, it's a good name for a guard :joy: - will check for the user roles, then if the user roles have the permissions to access this resource the guard will return true, else it will throw a ForbiddenException. For more information about the structure of roles see roles.interface.ts file or read the original documentation form accesscontrol library here.

    *note: for those who are asking what ACGuard stands for, it of course stands for Access Control Guard :smile:

  • UserRoles(<prop>): if you want to get access to the user roles directly, maybe you want to check it's roles manually instead of ACGuard doing it for you, then that decorator is what you are looking for. The decorator is really simple, it just return the req.user.roles value from the request object, but wait, what if the user roles doesn't exist in prop: role? We knew that you would ask this question, so you can pass an optional property key to the decorator to get it from the user object e.g @UserRoles('permissions') will return the req.user.permissions instead.

  • @InjectRolesBuilder(): if you hate the ACGuard - imo it's a good guard - and want to build your own Guard instead, you will likely need to access to the underlying RolesBuilder Object , then that decorator is for you; it will inject the Roles you have defined before, i.e the object passed to the AccessControlModule.forRoles(roles).

  1. Are you still there? Ok, that's it, you can go and run the application now, but wait, did someone asked for the AuthGuard? Ok let's discuss the LIMITATIONS.

Limitations

First of all, this module built with some assumptions

  1. The user object will exist in req.user
  2. It is up to you to build your own AuthGuard that will attach the user object to the req object, read more
  3. The AuthGuard must be registered before roles guard, in this case it's ACGuard, and of course you can combine the AuthGuard and ACGuard in one guard, and use it everywhere.

Secondly, i don't think these are limitations, since you can easily build your own guard and you don't need the built-in ones anymore.

CHANGELOG

See CHANGELOG for more information.

Contributing

You are welcome with this project for contributing, just make a PR.

Authors

  • Shady Khalifa - Initial work

See also the list of contributors who participated in this project.

License

This project is licensed under the MIT License - see the LICENSE.md file for details.

NPM DownloadsLast 30 Days