npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@flusys/nestjs-iam

v1.0.0

Published

Identity and Access Management (IAM) module for NestJS applications

Readme

IAM Package Guide

Package: @flusys/nestjs-iam Purpose: Identity and Access Management with RBAC, ABAC, and permission logic

Table of Contents


Overview

@flusys/nestjs-iam provides a comprehensive Identity and Access Management system:

  • Hierarchical Actions - Organize permissions in tree structures
  • Action Types - BACKEND (cached for guards), FRONTEND (returned to UI), BOTH
  • RBAC - Role-Based Access Control with validation
  • DIRECT - Direct user-to-action assignments with validation
  • Permission Mode Validation - Throws BadRequestException when using wrong mode
  • Company/Branch Scoping - Multi-tenant permissions with three-level granularity
  • Tenant-Aware Caching - Automatic cache invalidation with multi-tenant support

Package Hierarchy

@flusys/nestjs-core     <- Foundation
     |
@flusys/nestjs-shared   <- Shared utilities
     |
@flusys/nestjs-auth     <- User/Company management
     |
@flusys/nestjs-iam      <- Permission management (THIS PACKAGE)

Installation

npm install @flusys/nestjs-iam @flusys/nestjs-shared @flusys/nestjs-core

Module Configuration

With Auth Module (Recommended)

import { AuthModule } from '@flusys/nestjs-auth';
import { IAMModule } from '@flusys/nestjs-iam';

@Module({
  imports: [
    AuthModule.forRoot({
      includeIAM: true, // Required for single-tenant mode
      bootstrapAppConfig: {
        databaseMode: 'single',
        enableCompanyFeature: true,
      },
      config: { /* auth config */ },
    }),

    IAMModule.forRoot({
      global: true,
      includeController: true,
      bootstrapAppConfig: {
        databaseMode: 'single',
        enableCompanyFeature: true,
        permissionMode: 'FULL', // 'FULL' | 'RBAC' | 'DIRECT'
      },
    }),
  ],
})
export class AppModule {}

Async Configuration

IAMModule.forRootAsync({
  global: true,
  includeController: true,
  bootstrapAppConfig: {
    databaseMode: 'single',
    enableCompanyFeature: true,
    permissionMode: 'RBAC',
  },
  imports: [ConfigModule],
  useFactory: (config: ConfigService) => ({
    // IAM-specific options from config
  }),
  inject: [ConfigService],
})

Constants

// Injection Token
export const IAM_MODULE_OPTIONS = 'IAM_MODULE_OPTIONS';

Core Concepts

Actions

Actions represent permissions in the system:

| Field | Description | |-------|-------------| | name | Human-readable name | | code | Unique identifier (e.g., user.create) | | actionType | Category: backend, frontend, or both | | metadata | Additional data (icon, routerLink for frontend) | | parentId | Parent action for hierarchy | | serial | Display order |

Action Types

enum ActionType {
  BACKEND = 'backend',   // API endpoint permissions (cached for PermissionGuard)
  FRONTEND = 'frontend', // UI features (returned in my-permissions API)
  BOTH = 'both',         // Both backend and frontend
}

Roles

Roles are collections of actions:

| Field | Description | |-------|-------------| | name | Human-readable name | | description | Role description | | companyId | Company scope (when company feature enabled) | | isActive | Active status |

Permission Types

enum IamPermissionType {
  USER_ROLE = 'user_role',       // User assigned to role
  ROLE_ACTION = 'role_action',   // Role has action
  USER_ACTION = 'user_action',   // User has action directly
  COMPANY_ACTION = 'company_action', // Company action whitelist
}

Permission Resolution Order

  1. Company-Action Whitelist - Filter by company (if enabled)
  2. UserAction (DENY) - Explicit denials take precedence
  3. UserAction (GRANT) - Explicit grants
  4. UserRole -> RoleAction - Inherited from roles
  5. Action Permission Logic - Complex AND/OR rules

Permission Cascade Deletion

When removing company actions via removeCompanyActionsWithCascade(), the PermissionService performs cascade deletion:

Company Action Removed
        ↓
1. Delete COMPANY_ACTION permissions
        ↓
2. Find all roles in the company
        ↓
3. Delete ROLE_ACTION permissions for those actions
        ↓
4. Find all users in the company
        ↓
5. Delete USER_ACTION permissions for those actions
        ↓
6. Invalidate permission cache for affected users

Entities

Entity Groups

// Core entities (no company feature)
export const IAMCoreEntities = [Action, Role, UserIamPermission];

// Company-specific entities
export const IAMCompanyEntities = [RoleWithCompany, UserIamPermissionWithCompany];

// All entities
export const IAMAllEntities = [
  Action,
  Role,
  RoleWithCompany,
  UserIamPermission,
  UserIamPermissionWithCompany,
];

// Helper function
export function getIAMEntitiesByConfig(
  enableCompanyFeature: boolean,
  permissionMode: 'FULL' | 'RBAC' | 'DIRECT' = 'FULL',
): any[] {
  const entities: any[] = [Action];

  // Permission entity - always included
  if (enableCompanyFeature) {
    entities.push(UserIamPermissionWithCompany);
  } else {
    entities.push(UserIamPermission);
  }

  // Role entity - Only for RBAC or FULL mode (not DIRECT)
  if (permissionMode === 'RBAC' || permissionMode === 'FULL') {
    if (enableCompanyFeature) {
      entities.push(RoleWithCompany);
    } else {
      entities.push(Role);
    }
  }

  return entities;
}

ActionBase

Core action fields in action-base.entity.ts:

| Column | Type | Description | |--------|------|-------------| | readOnly | boolean | System-protected action | | name | varchar(255) | Action name | | code | varchar(255) | Unique code | | description | varchar(500) | Description | | actionType | enum | BACKEND, FRONTEND, BOTH | | permissionLogic | json | AND/OR rules | | parentId | uuid | Parent action ID | | serial | int | Display order | | isActive | boolean | Active status | | metadata | json | Additional data |

RoleBase

Core role fields in role-base.entity.ts:

| Column | Type | Description | |--------|------|-------------| | readOnly | boolean | System-protected role | | name | varchar(255) | Role name | | description | varchar(500) | Description | | isActive | boolean | Active status | | serial | int | Display order | | metadata | json | Additional data |

RoleWithCompany

Extends RoleBase with:

  • companyId (uuid) - Company scope

PermissionBase

Core permission fields in permission-base.entity.ts:

| Column | Type | Description | |--------|------|-------------| | permissionType | enum | USER_ROLE, ROLE_ACTION, etc. | | sourceType | enum | USER, ROLE, COMPANY | | sourceId | uuid | Source entity ID | | targetType | enum | ROLE, ACTION | | targetId | uuid | Target entity ID | | userId | uuid | User ID (nullable) | | validFrom | timestamp | Permission valid from | | validUntil | timestamp | Permission valid until | | reason | text | Reason for permission | | metadata | json | Additional data |

UserIamPermissionWithCompany

Extends PermissionBase with:

  • companyId (uuid) - Company scope
  • branchId (uuid) - Branch scope

Permission Granularity:

| Level | companyId | branchId | Use Case | |-------|-----------|----------|----------| | Global | null | null | Super admin across all companies | | Company-wide | set | null | Manager for ALL branches in company | | Branch-specific | set | set | Manager for specific branch only |


Permission Modes

Set in bootstrapAppConfig.permissionMode:

Permission Mode Enum

enum IAMPermissionMode {
  RBAC = 1,   // Role-Based: Action + RoleAction + UserRole
  DIRECT = 2, // Direct User Permissions: Action + UserAction
  FULL = 3,   // Both RBAC + Direct permissions
}

RBAC Mode (Role-Based)

User -> Role -> Action

Only role-based permissions. Users get permissions through assigned roles.

Note: Calling assignUserActions() in RBAC mode throws BadRequestException.

DIRECT Mode (Action-Based)

User -> Action

Only direct permissions. Users get actions assigned directly.

Note: Calling assignUserRoles() or assignRoleActions() in DIRECT mode throws BadRequestException.

FULL Mode (Combined)

User -> Role -> Action
User -> Action

Both RBAC and direct permissions. Default mode. All assignment methods available.


Permission Logic

Complex permission rules using AND/OR operators stored in permissionLogic field:

interface LogicNode {
  id: string;
  type: 'group' | 'action';
  operator?: 'AND' | 'OR';
  children?: LogicNode[];
  actionId?: string;
}

Example: User must have "employee" AND ("department-head" OR "hr-clearance")

const logic: LogicNode = {
  id: 'root',
  type: 'group',
  operator: 'AND',
  children: [
    { id: '1', type: 'action', actionId: 'employee-action-id' },
    {
      id: '2',
      type: 'group',
      operator: 'OR',
      children: [
        { id: '2-1', type: 'action', actionId: 'department-head' },
        { id: '2-2', type: 'action', actionId: 'hr-clearance' },
      ],
    },
  ],
};

Services

| Service | Scope | Description | |---------|-------|-------------| | ActionService | REQUEST | Action CRUD, tree queries | | RoleService | REQUEST | Role CRUD (RBAC/FULL mode only) | | PermissionService | REQUEST | Permission assignments, my-permissions | | PermissionCacheService | REQUEST | Cache management | | IAMConfigService | SINGLETON | IAM configuration access | | IAMDataSourceService | REQUEST | DataSource provider for multi-tenant |

ActionService

import { ActionService } from '@flusys/nestjs-iam';

// Create action
await actionService.insert({
  name: 'View Users',
  code: 'user.view',
  actionType: ActionType.BACKEND,
  parentId: parentAction.id,
});

// Get hierarchical tree
const tree = await actionService.getActionTree(user, 'search', true, false);

// Get actions for permission (company-filtered)
const actions = await actionService.getActionsForPermission(user);

RoleService

import { RoleService } from '@flusys/nestjs-iam';

// Create role
await roleService.insert({
  name: 'Administrator',
  description: 'Full system access',
  companyId: 'company-id',
});

PermissionService

import { PermissionService, PermissionAction } from '@flusys/nestjs-iam';

// Assign roles to user (RBAC/FULL mode only)
await permissionService.assignUserRoles({
  userId: 'user-id',
  companyId: 'company-id',
  branchId: null, // null = company-wide
  items: [{ id: 'role-id', action: PermissionAction.ADD }],
});

// Assign actions directly (DIRECT/FULL mode only)
await permissionService.assignUserActions({
  userId: 'user-id',
  companyId: 'company-id',
  branchId: 'branch-id',
  items: [{ id: 'action-id', action: PermissionAction.ADD }],
});

// Assign actions to role (RBAC/FULL mode only)
await permissionService.assignRoleActions({
  roleId: 'role-id',
  items: [{ id: 'action-id', action: PermissionAction.ADD }],
});

// Get user's permissions
const permissions = await permissionService.getMyPermissions(
  userId, branchId, companyId, parentCodes
);

PermissionCacheService

import { PermissionCacheService } from '@flusys/nestjs-iam';

// Set permissions
await cacheService.setPermissions(
  { userId, companyId, branchId, enableCompanyFeature: true },
  ['users.read', 'users.write']
);

// Invalidate user cache
await cacheService.invalidateUser('user-id', 'company-id', ['branch-id']);

// Invalidate role members
await cacheService.invalidateRole('role-id', userIds, 'company-id');

// Tenant-aware action code cache
await cacheService.setActionCodeMap(codeToIdMap, tenantId);
await cacheService.getActionIdsByCodes(['user.view'], tenantId);
await cacheService.invalidateActionCodeCache(tenantId);

Cache Key Formats:

permissions:user:{userId}                                        // Without company
permissions:company:{companyId}:branch:{branchId}:user:{userId}  // With company
action-codes:map                                                 // Single tenant
action-codes:tenant:{tenantId}:map                               // Multi-tenant

Helpers

PermissionModeHelper

Centralizes conversion from string to enum to prevent duplication:

import { PermissionModeHelper } from '@flusys/nestjs-iam';

// Convert string to enum
const mode = PermissionModeHelper.fromString('RBAC');
// Returns: IAMPermissionMode.RBAC

const mode = PermissionModeHelper.fromString(undefined);
// Returns: IAMPermissionMode.FULL (default)

// Convert enum to string
const str = PermissionModeHelper.toString(IAMPermissionMode.RBAC);
// Returns: 'RBAC'

Used by:

  • iam.module.ts (forRoot, forRootAsync)
  • iam-config.service.ts (getPermissionMode)
  • main.ts (Swagger setup)

validateCompanyAccess

Validates user access to a company for permission operations:

import { validateCompanyAccess } from '@flusys/nestjs-iam';

// In a controller or service
validateCompanyAccess(
  iamConfig,
  dto.companyId,
  user,
  'You do not have access to this company',
);
// Throws ForbiddenException if user.companyId !== dto.companyId

REST API Endpoints

Actions API

| Endpoint | Method | Description | |----------|--------|-------------| | /iam/actions/insert | POST | Create action | | /iam/actions/get-all | POST | List actions (paginated) | | /iam/actions/get/:id | GET | Get action by ID | | /iam/actions/tree | POST | Get hierarchical tree (ALL actions) | | /iam/actions/tree-for-permission | GET | Get company-filtered actions | | /iam/actions/update | POST | Update action | | /iam/actions/delete | POST | Delete action |

Roles API (RBAC/FULL mode only)

| Endpoint | Method | Description | |----------|--------|-------------| | /iam/roles/insert | POST | Create role | | /iam/roles/get-all | POST | List roles (paginated) | | /iam/roles/get/:id | GET | Get role by ID | | /iam/roles/update | POST | Update role | | /iam/roles/delete | POST | Delete role |

Permissions API

| Endpoint | Method | Modes | Description | |----------|--------|-------|-------------| | /iam/permissions/role-actions/assign | POST | RBAC, FULL | Assign actions to role | | /iam/permissions/role-actions/get | POST | RBAC, FULL | Get role actions | | /iam/permissions/user-roles/assign | POST | RBAC, FULL | Assign roles to user | | /iam/permissions/user-roles/get | POST | RBAC, FULL | Get user roles | | /iam/permissions/user-actions/assign | POST | DIRECT, FULL | Assign actions to user | | /iam/permissions/user-actions/get | POST | DIRECT, FULL | Get user actions | | /iam/permissions/company-actions/assign | POST | All (company enabled) | Company action whitelist | | /iam/permissions/company-actions/get | POST | All (company enabled) | Get company actions | | /iam/permissions/my-permissions | POST | All | Get current user's permissions |

Controller Registration by Mode:

| Mode | Controllers | |------|-------------| | DIRECT | ActionController, MyPermissionController, UserActionPermissionController | | RBAC | ActionController, MyPermissionController, RoleController, RolePermissionController | | FULL | All controllers |


Multi-Tenant Support

Company Feature Toggle

// config/app.config.ts
export const bootstrapAppConfig: IBootstrapAppConfig = {
  databaseMode: 'single', // or 'multi-tenant'
  enableCompanyFeature: true,
  permissionMode: 'FULL',
};

When enableCompanyFeature: true

  • companyId and branchId fields available in DTOs
  • Company-Action Permissions controller registered
  • Three-level permission granularity (Global, Company-wide, Branch-specific)
  • Uses UserIamPermissionWithCompany and RoleWithCompany entities

When enableCompanyFeature: false

  • Company endpoints NOT available (404)
  • companyId/branchId fields visible in Swagger but ignored
  • All permissions are global
  • Uses UserIamPermission and Role entities

Multi-Tenant Database Mode

When databaseMode: 'multi-tenant':

  • Each tenant has isolated database/schema
  • Action code cache is tenant-aware (separate cache per tenant)
  • Uses IAMDataSourceService for tenant-specific connections

Permission Merging

When getMyPermissions is called:

With branchId specified:

  1. Company-wide roles (branchId=null) + Branch-specific roles for that branch
  2. Actions from all merged roles
  3. Company-wide user actions + Branch-specific user actions for that branch

Without branchId (null):

  1. ALL roles for the user in the company (any branch)
  2. Actions from all roles
  3. ALL user actions for the company (any branch)

Result: Complete permission set without duplicates, filtered by company whitelist.


Permission Guard Integration

import { PermissionGuard } from '@flusys/nestjs-shared/guards';
import { RequirePermission, RequireAnyPermission } from '@flusys/nestjs-shared/decorators';

@Controller('users')
export class UserController {
  @RequirePermission('user.view')
  @Get()
  getUsers() {}

  @RequireAnyPermission('user.create', 'admin')
  @Post()
  createUser() {}
}

Best Practices

1. Action Code Naming

// Hierarchical naming for backend
'user.view', 'user.create', 'user.delete'

// UPPERCASE for frontend actions
'MENU_USERS', 'MENU_REPORTS', 'FEATURE_EXPORT'

2. Company-wide vs Branch-specific

// Company-wide for managers across ALL branches
{ companyId: 'company-a', branchId: null }

// Branch-specific for location-bound staff
{ companyId: 'company-a', branchId: 'branch-x' }

3. Use Roles for Common Patterns

// Create roles for common permission sets
const roles = [
  { name: 'Viewer', actions: ['*.view'] },
  { name: 'Editor', actions: ['*.view', '*.create', '*.update'] },
  { name: 'Admin', actions: ['*'] },
];

4. Use Direct Actions Sparingly

Direct actions should be exceptions and branch-specific overrides, not the primary permission mechanism.


API Reference

Exports

// Config
export { IAM_MODULE_OPTIONS } from './config';

// Modules
export { IAMModule } from './modules';

// Entities
export { Action } from './entities/action.entity';
export { ActionBase } from './entities/action-base.entity';
export { Role } from './entities/role.entity';
export { RoleBase } from './entities/role-base.entity';
export { RoleWithCompany } from './entities/role-with-company.entity';
export { PermissionBase } from './entities/permission-base.entity';
export { UserIamPermission } from './entities/user-iam-permission.entity';
export { UserIamPermissionWithCompany } from './entities/permission-with-company.entity';
export { IAMCoreEntities, IAMCompanyEntities, IAMAllEntities, getIAMEntitiesByConfig } from './entities';

// Services
export { ActionService } from './services/action.service';
export { RoleService } from './services/role.service';
export { PermissionService } from './services/permission.service';
export { PermissionCacheService } from './services/permission-cache.service';
export { IAMConfigService } from './services/iam-config.service';
export { IAMDataSourceService } from './services/iam-datasource.service';

// Helpers
export { PermissionModeHelper } from './helpers/permission-mode.helper';
export { validateCompanyAccess } from './helpers/company-access.helper';

// Enums
export { ActionType } from './enums/action-type.enum';
export { IAMPermissionMode } from './enums/permission-type.enum';

// Types
export { LogicNode } from './types/logic-node.type';

// DTOs
export * from './dtos';

// Interfaces
export * from './interfaces';

// Swagger Config
export { iamSwaggerConfig } from './docs';

Module Static Methods

// Configure module
IAMModule.forRoot(options?: IAMModuleOptions): DynamicModule
IAMModule.forRootAsync(options: IAMModuleAsyncOptions): DynamicModule
IAMModule.forFeature(options?: IAMModuleOptions): DynamicModule

Package Architecture

nestjs-iam/src/
├── config/
│   ├── iam.constants.ts           # IAM_MODULE_OPTIONS token
│   └── index.ts
├── modules/
│   └── iam.module.ts              # Main module with dynamic config
├── entities/
│   ├── action-base.entity.ts      # Action base fields
│   ├── action.entity.ts           # Action entity
│   ├── role-base.entity.ts        # Role base fields
│   ├── role.entity.ts             # Role (no company)
│   ├── role-with-company.entity.ts
│   ├── permission-base.entity.ts  # Permission base fields
│   ├── user-iam-permission.entity.ts
│   ├── permission-with-company.entity.ts
│   └── index.ts                   # Entity groups & getIAMEntitiesByConfig
├── services/
│   ├── action.service.ts          # Action CRUD
│   ├── role.service.ts            # Role CRUD
│   ├── permission.service.ts      # Permission management
│   ├── permission-cache.service.ts # Cache management
│   ├── iam-config.service.ts      # Configuration
│   └── iam-datasource.service.ts  # DataSource provider
├── controllers/
│   ├── action.controller.ts
│   ├── role.controller.ts
│   ├── my-permission.controller.ts
│   ├── role-permission.controller.ts
│   ├── user-action-permission.controller.ts
│   └── company-action-permission.controller.ts
├── helpers/
│   ├── company-access.helper.ts   # Company access validation
│   └── permission-mode.helper.ts  # String to enum conversion
├── dtos/
│   ├── action.dto.ts
│   ├── role.dto.ts
│   └── permission.dto.ts
├── interfaces/
│   ├── action.interface.ts
│   ├── role.interface.ts
│   └── iam-module-options.interface.ts
├── enums/
│   ├── action-type.enum.ts
│   └── permission-type.enum.ts
├── types/
│   └── logic-node.type.ts
├── docs/
│   └── iam-swagger.config.ts
└── index.ts

Last Updated: 2026-02-21