CASL Documentation
repository·master·Indexed 27 days ago
https://github.com/stalniy/caslAn isomorphic authorization library for JavaScript to define, manage, and share permissions across the frontend, backend, and database. Includes core functionality via @casl/ability and official integrations for Angular (@casl/angular), React (@casl/react), Vue (@casl/vue), and MongoDB/Mongoose (@casl/mongoose).
What's inside CASL
- CASL is an isomorphic authorization JavaScript library used to restrict access to resources. It is designed to be incrementally adoptable, scaling from simple claim-based authorization to complex subject and attribute-based authorization. It can be used on both the frontend and backend, making it easy to share permissions across UI components, API services, and database queries.
Understand the CASL Cookbook vs the Guide
masterThe CASL documentation is split into two distinct styles to serve different needs:
- The Guide: A narrative-driven introduction that builds knowledge incrementally. It uses simple examples to explain core features and assumes intermediate ES2015 JavaScript knowledge.
- The Cookbook: A collection of standalone recipes focused on specific, complex use cases. Recipes are deeper, more complex, and frequently use TypeScript, frontend/backend frameworks, and real-world scenarios. Each recipe is self-contained and does not assume knowledge from previous sections.
Prerequisites for Cookbook recipes: A basic understanding of JavaScript, npm/yarn, CommonJS, ES import/export, and Node.js module resolution is expected. Depending on the specific recipe, familiarity with certain frontend or backend libraries may be required.
Configure the deprecated Accessible Records plugin
masterThe
accessibleRecordsPluginaddsaccessibleBymethods to Mongoose queries and model statics. Note: This plugin is deprecated. The recommended approach is to use theaccessibleByhelper function instead.// Global registration const { accessibleRecordsPlugin } = require('@casl/mongoose'); const mongoose = require('mongoose'); mongoose.plugin(accessibleRecordsPlugin); // OR Local registration on a specific schema const mongoose = require('mongoose') const { accessibleRecordsPlugin } = require('@casl/mongoose') const Post = new mongoose.Schema({ title: String, author: String }) Post.plugin(accessibleRecordsPlugin) module.exports = mongoose.model('Post', Post)Use ForbiddenError to provide reasons for denied permissions
masterYou can attach a reason to an inverted rule using the
.because()method. When a permission check fails, you can catch aForbiddenErrorto retrieve this specific explanation.import { defineAbility, ForbiddenError } from '@casl/ability'; export default defineAbility((can, cannot) => { can('read', 'all'); cannot('read', 'all', { private: true }) .because('You are not allowed to read private information'); }); // Usage import ability from './defineAbility'; try { ForbiddenError.from(ability).throwUnlessCan('read', { private: true }); } catch (error) { if (error instanceof ForbiddenError) { console.log(error.message); // "You are not allowed to read private information" } }Distinguish between defining and checking permissions using custom names
masterTo avoid confusion between the
can/cannotmethods used to define rules and theability.can()method used to check permissions, you can use object destructuring to rename the rule-definition functions. This makes it explicitly clear whether you are currently in a definition context (using names likeallowandforbid) or a checking context (usingability.can()).You can apply this pattern using either
defineAbilityor theAbilityBuilderclass.import { defineAbility } from '@casl/ability'; // define abilities using explicit names const ability = defineAbility((allow, forbid) => { allow('read', 'Post'); forbid('read', 'Post', { private: true }); }); // check abilities using the standard can method ability.can('read', 'Post');Implement logical AND/OR/NOT using CASL rules
masterCASL does not include logical operators like
$and,$or,$nor, or$notin its condition language. Instead, you achieve this logic through rule composition:- AND: Combine multiple fields within a single condition object, or use multiple
canrules. All properties in a single condition object are checked withANDlogic. - OR: Define multiple
canrules for the same action and subject. If any rule matches, the permission is granted. - NOT: Use
cannotrules to negate permissions.
Note:
$norcannot be natively reproduced with standard rules; if required, you must customize the ability.- AND: Combine multiple fields within a single condition object, or use multiple
Access nested properties using dot notation
masterTo check conditions on nested properties of a subject, use dot notation within the condition keys. For example, to check a property inside a nested object, use'parent.child'. To check multiple criteria on elements within an array, use the$elemMatchoperator.Configure TypeScript for CASL Vue
masterTo get full type safety with your custom
AppAbility, you can use several approaches:- Augment Vue types: For global properties like
$canand$abilityto work with TypeScript, create a shim file. - Composition API: Pass your
AppAbilitytype to theuseAbilityhook:useAbility<AppAbility>(). - Options API: Cast the
ABILITY_TOKENto your customInjectionKey<AppAbility>to ensure injected instances are correctly typed.
// Augmenting Vue types (shims-ability.d.ts) import { AppAbility } from './AppAbility' declare module 'vue' { interface ComponentCustomProperties { $ability: AppAbility; $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean; } }// Options API with custom token import { InjectionKey } from 'vue'; import { ABILITY_TOKEN } from '@casl/vue'; export const TOKEN = ABILITY_TOKEN as InjectionKey<AppAbility>;- Augment Vue types: For global properties like
When to avoid roles with persisted permissions
masterThe pattern of persisting permissions in a database (roles with persisted permissions) should be avoided in the following scenarios:
- Static Permissions: If you have a predefined set of permissions for every role that is very unlikely to change.
- Project Uncertainty: If you are starting a new project and are unsure whether you will actually need to dynamically configure permissions.
Install @casl/ability
masterTo use CASL in a Node.js project, install@casl/abilityas a dependency via npm or yarn.Configure AppModule for CASL
masterTo use CASL pipes or services in your templates, import the necessary modules and provide the
Abilityinstance in yourAppModule. You typically usecreateMongoAbility()to initialize the ability instance.import { NgModule } from '@angular/core'; import { AblePipe } from '@casl/angular'; import { createMongoAbility, Ability } from '@casl/ability'; @NgModule({ imports: [ // other modules AblePipe ], providers: [ { provide: Ability, useValue: createMongoAbility() } ] }) export class AppModule {}Install @casl/vue and @casl/ability
masterTo integrate CASL with a Vue 3 application, install both
@casl/vueand@casl/abilityusing your preferred package manager.npm install @casl/vue @casl/ability # or yarn add @casl/vue @casl/ability # or pnpm add @casl/vue @casl/ability