Isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access
npx @tessl/cli install tessl/npm-casl--ability@6.7.0CASL (pronounced "castle") is an isomorphic authorization JavaScript library that enables fine-grained access control through a declarative rule-based approach. It supports both simple claim-based authorization and complex subject and attribute-based authorization (ABAC), providing a fluent API for defining permissions with MongoDB-style query operators.
npm install @casl/abilityimport {
createMongoAbility,
defineAbility,
AbilityBuilder,
PureAbility,
ForbiddenError,
subject,
detectSubjectType,
createAliasResolver,
wrapArray,
mongoQueryMatcher,
fieldPatternMatcher
} from "@casl/ability";
// Type imports
import type {
AnyMongoAbility,
MongoAbility,
AbilityTuple,
Subject,
SubjectType,
Abilities,
ForcedSubject,
CanParameters
} from "@casl/ability";
// HKT namespace import
import * as hkt from "@casl/ability/hkt";For CommonJS:
const {
createMongoAbility,
defineAbility,
AbilityBuilder,
PureAbility,
ForbiddenError,
subject,
detectSubjectType,
createAliasResolver,
wrapArray,
mongoQueryMatcher,
fieldPatternMatcher
} = require("@casl/ability");import { createMongoAbility, defineAbility, AbilityBuilder } from "@casl/ability";
// Method 1: Factory function with rules array
const ability = createMongoAbility([
{ action: 'read', subject: 'Post' },
{ action: 'create', subject: 'Post', conditions: { authorId: '${user.id}' } },
{ action: 'delete', subject: 'Post', inverted: true, conditions: { published: true } }
]);
// Method 2: Functional DSL
const ability2 = defineAbility((can, cannot) => {
can('read', 'Post');
can('create', 'Post');
cannot('delete', 'Post', { published: true });
});
// Method 3: Builder pattern
const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
can('read', 'Post');
can('update', 'Post', { authorId: 'user123' });
cannot('delete', 'Post', { published: true });
const ability3 = build();
// Check permissions
if (ability.can('read', 'Post')) {
console.log('Can read posts');
}
if (ability.cannot('delete', { __type: 'Post', published: true })) {
console.log('Cannot delete published posts');
}CASL is built around several key components:
PureAbility (base class) and specialized ability classes for different use casesAbilityBuilder for fluent rule construction and defineAbility functional DSLForbiddenError with detailed context for permission violationsCreate and manage ability instances with rule-based permissions. The foundation for all authorization logic.
function createMongoAbility<T = AnyMongoAbility>(
rules?: RawRuleOf<T>[],
options?: AbilityOptionsOf<T>
): T;
function defineAbility<T = AnyMongoAbility>(
define: (can: DefineRule<T>, cannot: DefineRule<T>) => void | Promise<void>,
options?: AbilityOptionsOf<T>
): T | Promise<T>;
class PureAbility<A extends Abilities = Abilities, Conditions = AnyObject> {
can(...args: CanParameters<A>): boolean;
cannot(...args: CanParameters<A>): boolean;
relevantRuleFor(...args: CanParameters<A>): Rule<A, Conditions> | null;
update(rules: RawRuleFrom<A, Conditions>[]): this;
}Check permissions against subjects with optional conditions and field-level access control.
type CanParameters<A extends Abilities> =
| [action: A, subject?: Subject]
| [action: A, subject: Subject, field?: string];
interface Rule<A extends Abilities, Conditions> {
readonly action: string | string[];
readonly subject?: string | string[];
readonly conditions?: Conditions;
readonly fields?: string[];
readonly inverted: boolean;
readonly reason?: string;
matchesConditions(object: any): boolean;
matchesField(field: string): boolean;
}Build complex permission rules using fluent builder pattern or functional DSL approach.
class AbilityBuilder<T extends AnyAbility> {
rules: RawRuleOf<T>[];
can: DefineRule<T>;
cannot: DefineRule<T>;
build(options?: AbilityOptionsOf<T>): T;
}
type DefineRule<T extends AnyAbility> = (
action: Parameters<T['can']>[0],
subject?: Parameters<T['can']>[1],
conditionsOrFields?: any,
fields?: string | string[]
) => void;Handle permission violations with detailed error context and customizable messages.
class ForbiddenError<T extends AnyAbility = AnyAbility> extends Error {
readonly ability: T;
readonly action: string;
readonly subject: any;
readonly field?: string;
readonly subjectType: string;
static setDefaultMessage(messageOrFn: string | ErrorMessageFactory): void;
static from<U extends AnyAbility>(ability: U): ForbiddenError<U>;
setMessage(message: string): this;
throwUnlessCan(...args: Parameters<T['can']>): void;
unlessCan(...args: Parameters<T['can']>): this | undefined;
}Advanced features for serialization, database integration, and field-level access analysis.
// Rule serialization (from @casl/ability/extra)
function packRules<T extends RawRule<any, any>>(
rules: T[],
packSubject?: (subject: any) => any
): PackRule<T>[];
function unpackRules<T extends RawRule<any, any>>(
rules: PackRule<T>[],
unpackSubject?: (subject: any) => any
): T[];
// Field analysis
function permittedFieldsOf<T extends AnyAbility>(
ability: T,
action: Parameters<T['can']>[0],
subject: Parameters<T['can']>[1],
options?: { fieldsFrom?: any }
): string[];
// Query generation
function rulesToQuery<T extends AnyAbility, Q>(
ability: T,
action: Parameters<T['can']>[0],
subjectType: string,
convert: (rule: Rule<any, any>) => Q
): Q | null;Core utility functions for subject type handling, array processing, and alias management.
/**
* Set subject type on object for explicit type detection
* @param type - Subject type name to set
* @param object - Object to tag with subject type
* @returns Tagged object with forced subject type
*/
function subject<T extends string, U extends Record<PropertyKey, any>>(
type: T,
object: U
): U & ForcedSubject<T>;
/**
* Detect subject type from object instance
* @param object - Object to detect type from
* @returns String representation of subject type
*/
function detectSubjectType(object: Exclude<Subject, SubjectType>): string;
/**
* Create action alias resolver for expanding action shortcuts
* @param aliasMap - Map of aliases to action arrays
* @param options - Optional configuration
* @returns Function that expands aliases to full action arrays
*/
function createAliasResolver(
aliasMap: AliasesMap,
options?: AliasResolverOptions
): (action: string | string[]) => string[];
/**
* Wrap value in array if not already an array
* @param value - Value to wrap
* @returns Array containing the value
*/
function wrapArray<T>(value: T[] | T): T[];
// Matcher functions
function mongoQueryMatcher<T = AnyObject>(conditions: MongoQuery<T>): MatchConditions<T>;
function fieldPatternMatcher(fields: string[]): MatchField<string>;
// Support types
interface AliasResolverOptions {
skipValidate?: boolean;
anyAction?: string;
}// Basic utility types
type AnyClass<ReturnType = any> = new (...args: any[]) => ReturnType;
type AnyObject = Record<PropertyKey, unknown>;
type AnyRecord = Record<PropertyKey, any>;
// Subject and ability types
type SubjectClass<N extends string = string> = AnyClass & { modelName?: N };
type SubjectType = string | SubjectClass;
type Subject = AnyRecord | SubjectType;
type AbilityTuple<X extends string = string, Y extends Subject = Subject> = [X, Y];
type Abilities = AbilityTuple | string;
// Rule definition types
interface RawRule<A extends Abilities, C = AnyObject> {
action: A extends readonly [infer Action, any] ? Action : A;
subject?: A extends readonly [any, infer S] ? S : string;
conditions?: C;
fields?: string | string[];
inverted?: boolean;
reason?: string;
}
// Type utilities
type Normalize<T extends Abilities> = T extends AbilityTuple ? T : [T, string];
type IfString<T, U> = T extends string ? U : never;
// Tagged interface support
interface ForcedSubject<T> {
readonly __caslSubjectType__: T;
}
type TaggedInterface<T extends string> = ForcedSubject<T> |
{ readonly kind: T } |
{ readonly __typename: T };
type TagName<T> = T extends TaggedInterface<infer U> ? U : never;
type ExtractSubjectType<S extends Subject> = Extract<S, SubjectType> | TagName<S>;
type SubjectClassWithCustomName<T> = AnyClass & { modelName: T };
type InferSubjects<T, IncludeTagName extends boolean = false> =
T | (T extends AnyClass<infer I>
? I | (IncludeTagName extends true
? T extends SubjectClassWithCustomName<infer Name> ? Name : TagName<I>
: never)
: TagName<T>);
// Parameter types
type Fn = (...args: any[]) => any;
type AbilityParameters<
T extends Abilities,
TupleFunction extends Fn,
StringFunction extends Fn = () => 0,
Else = IfString<T, Parameters<StringFunction>>
> = T extends AbilityTuple ? Parameters<TupleFunction> : Else;
type CanParameters<T extends Abilities, IncludeField extends boolean = true> =
AbilityParameters<
T,
T extends AbilityTuple
? IncludeField extends true
? (action: T[0], subject: T[1], field?: string) => 0
: (action: T[0], subject: T[1]) => 0
: never,
(action: Extract<T, string>) => 0
>;
// MongoDB integration types
type MongoQuery<T = AnyObject> = Record<string, any>;
type MongoAbility<
A extends AbilityTuple = AbilityTuple,
C extends MongoQuery = MongoQuery
> = PureAbility<A, C>;
type AnyMongoAbility = MongoAbility<any, any>;
// Matcher types
type MatchConditions<T extends {} = AnyRecord> = {
(object: T): boolean;
ast?: any; // Condition type from @ucast/mongo2js
};
type ConditionsMatcher<T> = (conditions: T) => MatchConditions;
type MatchField<T extends string> = (field: T) => boolean;
type FieldMatcher = <T extends string>(fields: T[]) => MatchField<T>;
// Rule index and event types (exported from main module)
interface Generics<T> {
abilities: any;
conditions: any;
}
type RawRuleOf<T extends AnyAbility> = RawRule<Generics<T>['abilities'], Generics<T>['conditions']>;
type RuleOf<T extends AnyAbility> = Rule<Generics<T>['abilities'], Generics<T>['conditions']>;
interface UpdateEvent<T extends AnyAbility> {
rules: RawRuleOf<T>[];
}
type EventHandler<T> = (event: T) => void;
type Unsubscribe = () => void;
// Utility types
type AliasesMap = Record<string, string | string[]>;