Convert Figma logo to code with AI

stalniy logocasl

CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access

5,871
265
5,871
35

Top Related Projects

Your data, your control. Fully open source, authentication and authorization. No lock-ins. Deployment in Railway in 120 seconds || Spin a docker image as a micro-service in your infra. Built in login page and Admin panel out of the box.

17,484

An authorization library that supports access control models like ACL, RBAC, ABAC in Golang: https://discord.gg/S5UjpzGZjN

The authorization Gem for Ruby on Rails.

15,024

The API and real-time application framework

Role and Attribute based Access Control for Nestjs 🔐

Role and Attribute based Access Control for Node.js

Quick Overview

CASL (Conditions And Subject-based Logic) is an isomorphic authorization library that restricts what resources a given user is allowed to access. It's designed to be flexible, scalable, and adaptable to various JavaScript environments, including both frontend and backend applications.

Pros

  • Versatile and framework-agnostic, working with popular JavaScript frameworks and ORMs
  • Supports both attribute and subject-based authorization
  • Provides a declarative and expressive API for defining permissions
  • Offers TypeScript support for improved type safety

Cons

  • Learning curve for complex authorization scenarios
  • May introduce overhead for simple authorization use cases
  • Documentation can be overwhelming for beginners
  • Limited built-in integrations with some less popular frameworks or databases

Code Examples

  1. Defining abilities:
import { AbilityBuilder, Ability } from '@casl/ability';

function defineAbilityFor(user) {
  const { can, cannot, build } = new AbilityBuilder(Ability);

  if (user.isAdmin) {
    can('manage', 'all');
  } else {
    can('read', 'Post');
    can('create', 'Comment');
  }

  return build();
}
  1. Checking permissions:
const ability = defineAbilityFor(user);

if (ability.can('update', post)) {
  // User can update the post
} else {
  // User cannot update the post
}
  1. Using with React components:
import { Can } from '@casl/react';

function PostList({ posts }) {
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>
          {post.title}
          <Can I="update" this={post}>
            <button>Edit</button>
          </Can>
        </li>
      ))}
    </ul>
  );
}

Getting Started

  1. Install CASL:
npm install @casl/ability
  1. Define abilities:
import { AbilityBuilder, Ability } from '@casl/ability';

const ability = AbilityBuilder.define((can, cannot) => {
  can('read', 'Post');
  can('create', 'Comment');
});
  1. Use in your application:
if (ability.can('read', 'Post')) {
  // Allow user to read posts
}

Competitor Comparisons

Your data, your control. Fully open source, authentication and authorization. No lock-ins. Deployment in Railway in 120 seconds || Spin a docker image as a micro-service in your infra. Built in login page and Admin panel out of the box.

Pros of Authorizer

  • Provides a complete authentication and authorization solution, including user management and OAuth integration
  • Offers a user-friendly dashboard for managing users and permissions
  • Supports multiple database options (PostgreSQL, MySQL, SQLite)

Cons of Authorizer

  • Less flexible for complex authorization scenarios compared to CASL's fine-grained permissions
  • Steeper learning curve due to its comprehensive feature set
  • May be overkill for projects that only need simple authorization rules

Code Comparison

CASL:

import { AbilityBuilder, Ability } from '@casl/ability'

const ability = new Ability(
  AbilityBuilder.define((can, cannot) => {
    can('read', 'Post')
    cannot('delete', 'Post', { published: true })
  })
)

Authorizer:

import { Authorizer } from '@authorizerdev/authorizer-js'

const authorizer = new Authorizer({
  authorizerURL: 'https://your-authorizer-instance.com',
  clientID: 'your-client-id'
})

const isAuthorized = await authorizer.isAuthorized('read:posts')

Summary

CASL focuses on flexible, attribute-based authorization with a simple API, while Authorizer provides a more comprehensive authentication and authorization solution. CASL is better suited for projects requiring complex, fine-grained permissions, whereas Authorizer excels in scenarios needing a complete auth system with user management and OAuth support.

17,484

An authorization library that supports access control models like ACL, RBAC, ABAC in Golang: https://discord.gg/S5UjpzGZjN

Pros of Casbin

  • Supports multiple access control models (ACL, RBAC, ABAC, etc.)
  • Language-agnostic with implementations in various programming languages
  • Provides a powerful policy language (PERM) for defining complex rules

Cons of Casbin

  • Steeper learning curve due to its flexibility and complexity
  • May be overkill for simpler authorization scenarios
  • Requires more setup and configuration compared to CASL

Code Comparison

CASL:

const ability = defineAbility((can) => {
  can('read', 'Article');
  can('update', 'Article', { authorId: 1 });
});

Casbin:

e := casbin.NewEnforcer("model.conf", "policy.csv")
sub := "alice"
obj := "data1"
act := "read"
allowed := e.Enforce(sub, obj, act)

Summary

Casbin offers more flexibility and supports multiple access control models, making it suitable for complex authorization scenarios across various programming languages. However, it has a steeper learning curve and requires more setup compared to CASL. CASL provides a simpler, more intuitive API for defining permissions, making it easier to implement basic to moderate authorization rules, especially in JavaScript-based projects.

The authorization Gem for Ruby on Rails.

Pros of CanCanCan

  • Ruby-specific, deeply integrated with Rails ecosystem
  • Simpler syntax for basic authorization rules
  • Extensive documentation and community support

Cons of CanCanCan

  • Limited to Ruby/Rails applications
  • Less flexible for complex authorization scenarios
  • Potential performance issues with large rulesets

Code Comparison

CanCanCan:

class Ability
  include CanCan::Ability
  def initialize(user)
    can :read, Post
    can :manage, Post, user_id: user.id
  end
end

CASL:

import { AbilityBuilder, Ability } from '@casl/ability'

function defineAbilityFor(user) {
  return new Ability(AbilityBuilder.define(can => {
    can('read', 'Post')
    can('manage', 'Post', { userId: user.id })
  }))
}

Key Differences

  • CASL is framework-agnostic and supports multiple programming languages
  • CanCanCan uses a more Ruby-like DSL, while CASL adopts a JavaScript-friendly approach
  • CASL offers more granular control over permissions and supports isomorphic applications
  • CanCanCan is more tightly integrated with Rails' conventions and helpers

Both libraries aim to simplify authorization logic, but CASL provides greater flexibility across different environments, while CanCanCan excels in Ruby-specific scenarios.

15,024

The API and real-time application framework

Pros of Feathers

  • Full-featured real-time web framework with built-in services, authentication, and database adapters
  • Supports multiple databases and ORMs out of the box
  • Extensive plugin ecosystem for additional functionality

Cons of Feathers

  • Steeper learning curve due to its comprehensive nature
  • May be overkill for simple applications or those not requiring real-time features
  • Less flexible for fine-grained authorization control compared to CASL

Code Comparison

CASL (defining abilities):

import { AbilityBuilder, Ability } from '@casl/ability'

const ability = AbilityBuilder.define((can, cannot) => {
  can('read', 'Post')
  cannot('delete', 'Post', { published: true })
})

Feathers (setting up a service):

const feathers = require('@feathersjs/feathers')
const app = feathers()

app.use('/posts', {
  async find() { /* ... */ },
  async create(data) { /* ... */ }
})

While CASL focuses on defining and managing permissions, Feathers provides a complete framework for building real-time applications. CASL offers more granular control over authorization, while Feathers excels in rapid development of full-stack applications with built-in real-time capabilities.

Role and Attribute based Access Control for Nestjs 🔐

Pros of nest-access-control

  • Specifically designed for NestJS, offering seamless integration
  • Utilizes a role-based access control (RBAC) approach, which can be more intuitive for some developers
  • Provides decorators for easy implementation in NestJS controllers

Cons of nest-access-control

  • Less flexible than CASL, as it's primarily focused on RBAC
  • Limited to NestJS applications, whereas CASL is framework-agnostic
  • Smaller community and fewer resources compared to CASL

Code Comparison

nest-access-control:

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

CASL:

ability.can('read', 'Article')

if (ability.can('read', 'Article')) {
  // Perform action
}

Both libraries offer ways to define and check permissions, but CASL provides a more flexible and expressive API that can be used across different frameworks and environments. nest-access-control, while more limited in scope, offers tighter integration with NestJS through decorators and guards.

Role and Attribute based Access Control for Node.js

Pros of accesscontrol

  • Simpler API and easier to get started with for basic use cases
  • Built-in support for role hierarchies
  • Lightweight with no dependencies

Cons of accesscontrol

  • Less flexible for complex authorization scenarios
  • Limited support for dynamic permissions
  • Fewer integrations with popular frameworks and ORMs

Code Comparison

accesscontrol:

const ac = new AccessControl();
ac.grant('user').createOwn('article');
ac.can('user').createOwn('article').granted; // true

CASL:

const ability = defineAbility((can) => {
  can('create', 'Article', { authorId: user.id });
});
ability.can('create', 'Article', { authorId: user.id }); // true

Key Differences

  • CASL offers more granular control over permissions and conditions
  • accesscontrol focuses on role-based access control, while CASL supports attribute-based access control
  • CASL provides better TypeScript support and type inference
  • accesscontrol has a simpler learning curve for basic scenarios
  • CASL offers more extensive documentation and examples for complex use cases

Both libraries are actively maintained and have their strengths. Choose accesscontrol for simpler role-based access control needs, and CASL for more complex and flexible authorization requirements.

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

CASL

Do you like this package?

Support Ukraine 🇺🇦

CASL logo

Financial Contributors on Open Collective build CASL codecov CASL Join the chat at https://gitter.im/stalniy-casl/casl

CASL (pronounced /ˈkæsəl/, like castle) is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access. It's designed to be incrementally adoptable and can easily scale between a simple claim based and fully featured subject and attribute based authorization. It makes it easy to manage and share permissions across UI components, API services, and database queries.

Heavily inspired by cancan.

Features

  • Versatile
    An incrementally adoptable and can easily scale between a simple claim based and fully featured subject and attribute based authorization.
  • Isomorphic
    Can be used on frontend and backend and complementary packages make integration with major Frontend Frameworks and Backend ORMs effortless
  • TypeSafe
    Written in TypeScript, what makes your apps safer and developer experience more enjoyable
  • Tree shakable
    The core is only 6KB mingzipped and can be even smaller!
  • Declarative
    Thanks to declarative rules, you can serialize and share permissions between UI and API or microservices

Ecosystem

ProjectStatusDescriptionSupported envinronemnts
@casl/ability@casl/ability-statusCASL's core packagenodejs 8+ and ES5 compatible browsers (IE 9+)
@casl/mongoose@casl/mongoose-statusintegration with Mongoosenodejs 8+
@casl/prisma@casl/prisma-statusintegration with Prismanodejs 12+
@casl/angular@casl/angular-statusintegration with AngularIE 9+
@casl/react@casl/react-statusintegration with ReactIE 9+
@casl/vue@casl/vue-statusintegration with VueIE 11+ (uses WeakMap)
@casl/aurelia@casl/aurelia-statusintegration with AureliaIE 11+ (uses WeakMap)

Documentation

A lot of detailed information about CASL, integrations and examples can be found in documentation.

Have a question?

Ask it in chat or on stackoverflow. Please don't ask questions in issues, the issue list of this repo is exclusively for bug reports and feature requests. Questions in the issue list may be closed immediately without answers.

CASL crash course

CASL operates on the abilities level, that is what a user can actually do in the application. An ability itself depends on the 4 parameters (last 3 are optional):

  1. User Action
    Describes what user can actually do in the app. User action is a word (usually a verb) which depends on the business logic (e.g., prolong, read). Very often it will be a list of words from CRUD - create, read, update and delete.
  2. Subject
    The subject or subject type which you want to check user action on. Usually this is a business (or domain) entity name (e.g., Subscription, BlogPost, User).
  3. Conditions
    An object or function which restricts user action only to matched subjects. This is useful when you need to give a permission on resources created by a user (e.g., to allow user to update and delete own BlogPost)
  4. Fields
    Can be used to restrict user action only to matched subject's fields (e.g., to allow moderator to update hidden field of BlogPost but not update description or title)

Using CASL you can describe abilities using regular and inverted rules. Let's see how

Note: all the examples below will be written in TypeScript but CASL can be used in similar way in ES6+ and Nodejs environments.

1. Define Abilities

Lets define Ability for a blog website where visitors:

  • can read blog posts
  • can manage (i.e., do anything) own posts
  • cannot delete a post if it was created more than a day ago
import { AbilityBuilder, createMongoAbility } from '@casl/ability'
import { User } from '../models'; // application specific interfaces

/**
 * @param user contains details about logged in user: its id, name, email, etc
 */
function defineAbilitiesFor(user: User) {
  const { can, cannot, build } = new AbilityBuilder(createMongoAbility);

  // can read blog posts
  can('read', 'BlogPost');
  // can manage (i.e., do anything) own posts
  can('manage', 'BlogPost', { author: user.id });
  // cannot delete a post if it was created more than a day ago
  cannot('delete', 'BlogPost', {
    createdAt: { $lt: Date.now() - 24 * 60 * 60 * 1000 }
  });

  return build();
});

Do you see how easily business requirements were translated into CASL's rules?

Note: you can use class instead of string as a subject type (e.g., can('read', BlogPost))

And yes, Ability class allow you to use some MongoDB operators to define conditions. Don't worry if you don't know MongoDB, it's not required and explained in details in Defining Abilities

2. Check Abilities

Later on you can check abilities by using can and cannot methods of Ability instance.

// in the same file as above
import { ForbiddenError } from '@casl/ability';

const user = getLoggedInUser(); // app specific function
const ability = defineAbilitiesFor(user);

class BlogPost { // business entity
  constructor(props) {
    Object.assign(this, props);
  }
}

// true if ability allows to read at least one Post
ability.can('read', 'BlogPost');
// the same as
ability.can('read', BlogPost);

// true, if user is the author of the blog post
ability.can('manage', new BlogPost({ author: user.id }));

// true if there is no ability to read this particular blog post
const ONE_DAY = 24 * 60 * 60 * 1000;
const postCreatedNow = new BlogPost({ createdAt: new Date() });
const postCreatedAWeekAgo = new BlogPost({ createdAt: new Date(Date.now() - 7 * ONE_DAY) });

// can delete if it's created less than a day ago
ability.can('delete', postCreatedNow); // true
ability.can('delete', postCreatedAWeekAgo); // false

// you can even throw an error if there is a missed ability
ForbiddenError.from(ability).throwUnlessCan('delete', postCreatedAWeekAgo);

Of course, you are not restricted to use only class instances in order to check permissions on objects. See Introduction for the detailed explanation.

3. Database integration

CASL has a complementary package @casl/mongoose which provides easy integration with MongoDB and mongoose.

import { accessibleRecordsPlugin } from '@casl/mongoose';
import mongoose from 'mongoose';

mongoose.plugin(accessibleRecordsPlugin);

const user = getUserLoggedInUser(); // app specific function

const ability = defineAbilitiesFor(user);
const BlogPost = mongoose.model('BlogPost', mongoose.Schema({
  title: String,
  author: mongoose.Types.ObjectId,
  content: String,
  createdAt: Date,
  hidden: { type: Boolean, default: false }
}))

// returns mongoose Query, so you can chain it with other conditions
const posts = await BlogPost.accessibleBy(ability).where({ hidden: false });

// you can also call it on existing query to enforce permissions
const hiddenPosts = await BlogPost.find({ hidden: true }).accessibleBy(ability);

// you can even pass the action as a 2nd parameter. By default action is "read"
const updatablePosts = await BlogPost.accessibleBy(ability, 'update');

See Database integration for details.

4. Advanced usage

CASL is incrementally adoptable, that means you can start your project with simple claim (or action) based authorization and evolve it later, when your app functionality evolves.

CASL is composable, that means you can implement alternative conditions matching (e.g., based on joi, ajv or pure functions) and field matching (e.g., to support alternative syntax in fields like addresses.*.street or addresses[0].street) logic.

See Advanced usage for details.

5. Examples

Looking for examples? Check CASL examples repository.

Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing.

If you'd like to help us sustain our community and project, consider to become a financial contributor on Open Collective

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

License

MIT License

Copyright (c) 2017-present, Sergii Stotskyi

NPM DownloadsLast 30 Days