@indrajitsir/nest-auth-core
v0.2.0
Published
Persistence-agnostic authorization core for NestJS
Maintainers
Readme
@indrajitsir/nest-auth-core
Persistence-agnostic authorization core for NestJS.
It provides the @Authorization decorator, AuthorizationGuard,
AuthorizationEngine, PolicyEvaluator, and the AuthorizationProvider
contract — with zero SQL, ORM, or persistence code inside. Pair it with an
adapter such as @indrajitsir/nest-auth-sql-adapter or write
your own provider.
Features
- Role-based authorization (RBAC) —
@Authorization("ADMIN", "HR_MANAGER")declares which roles may access an endpoint (v0.2.0). - Resource/action authorization —
@Authorization({ resource, action })remains fully supported (v0.1.0). - Fail-safe DENY — missing user, missing roles, or a failed lookup never grant access.
- Provider abstraction — the engine never touches the database; any
AuthorizationProviderimplementation plugs in. - TypeScript-first — strict types,
Actionenum, normalizedAuthorizationResult.
Installation
npm install @indrajitsir/nest-auth-corePeer dependencies (install them if your project does not already have them):
npm install @nestjs/common @nestjs/core reflect-metadataQuickstart
The core package needs a provider to make decisions. The example below uses the SQL adapter (see its README) — the wiring is identical for any provider.
// app.module.ts
import { Module } from "@nestjs/common";
import { AuthorizationModule } from "@indrajitsir/nest-auth-core";
import {
AuthorizationSqlModule,
SqlAuthorizationProvider,
} from "@indrajitsir/nest-auth-sql-adapter";
@Module({
imports: [
AuthorizationModule.forRoot({
provider: SqlAuthorizationProvider,
global: true,
}),
AuthorizationSqlModule.forRoot({
dataSource, // your TypeORM DataSource
schema: {
roleMapping: {
table: "role_base_access_mapping",
userIdColumn: "user_id",
roleIdColumn: "role_id",
},
role: {
table: "mapping_access",
roleIdColumn: "role_id",
roleNameColumn: "role_name",
},
},
}),
],
})
export class AppModule {}// employees.controller.ts
import { Controller, Get, UseGuards } from "@nestjs/common";
import { Authorization, AuthorizationGuard } from "@indrajitsir/nest-auth-core";
@Controller("employees")
@UseGuards(AuthorizationGuard)
export class EmployeesController {
@Authorization("ADMIN", "HR_MANAGER") // ADMIN OR HR_MANAGER
@Get()
findAll() {
return [];
}
}That is everything: the guard reads the decorator metadata, resolves the
current user's roles through the provider, and throws a ForbiddenException
when access is not allowed.
Role-based authorization (v0.2.0)
Declare the roles allowed to access an endpoint. Multiple roles mean OR:
@Authorization("ADMIN") // single role
@Authorization("ADMIN", "HR_MANAGER") // ADMIN OR HR_MANAGER
@Authorization(["ADMIN", "HR_MANAGER"]) // array formThe engine resolves the authenticated user's role names
(user_id → role_id → role_name) through the provider and allows the request
when the sets intersect:
requiredRoles ∩ userRoles ≠ ∅ → ALLOW
requiredRoles ∩ userRoles = ∅ → DENY (403)The decorator only writes metadata — it never queries the database itself.
Resource/action authorization (v0.1.0)
The original API is still supported and coexists with role-based policies:
@Authorization({ resource: "employee", action: "READ" })
@Authorization({ resource: "employee", action: Action.DELETE })or split across two decorators:
@Resource("employee")
@Allow("DELETE")The policy evaluator dispatches automatically based on the policy shape, so both forms can be used in the same application.
Module options
AuthorizationModule.forRoot({
provider: SqlAuthorizationProvider, // a class, Provider, or FactoryProvider
global: true, // register the guard/engine app-wide
})| Option | Type | Description |
| --- | --- | --- |
| provider | Type<AuthorizationProvider> \| Provider \| FactoryProvider | The authorization mechanism used to make decisions. |
| global | boolean | When true, the guard/engine are available in every feature module (recommended). |
The module exports AuthorizationGuard, AuthorizationEngine,
PolicyEvaluator, AuthorizationMetadataResolver, and the
AUTHORIZATION_PROVIDER token.
Writing a custom provider
Any class implementing AuthorizationProvider works:
import {
AuthorizationContext,
AuthorizationPolicy,
AuthorizationProvider,
AuthorizationResult,
} from "@indrajitsir/nest-auth-core";
export class MyProvider implements AuthorizationProvider {
// Used for resource/action policies (@Authorization({ resource, action })).
async authorize(
context: AuthorizationContext,
policy: AuthorizationPolicy,
): Promise<AuthorizationResult> {
const roles = await this.resolveRoles(context);
// ... your logic ...
return { allowed: true };
}
// Used for role-based policies (@Authorization("ADMIN", ...)).
// Optional — role-based policies DENY when the provider cannot resolve roles.
async resolveRoles(context: AuthorizationContext): Promise<string[]> {
const userId = context.user?.id;
if (!userId) {
return [];
}
return this.roleService.findNamesByUserId(userId);
}
}Register it:
AuthorizationModule.forRoot({
provider: MyProvider,
global: true,
})How authorization flows
HTTP Request
↓
AuthorizationGuard reads metadata, builds the AuthorizationContext
↓
AuthorizationEngine orchestrates evaluation
↓
PolicyEvaluator dispatches by policy shape
├── role policy → provider.resolveRoles(context) → set intersection
└── resource/action → provider.authorize(context, policy)
↓
AuthorizationResult { allowed, reason? }Fail-safe behavior
The library never grants access because of a missing lookup or an exception:
| Situation | Result |
| --- | --- |
| No @Authorization metadata on the endpoint | Guard passes through (endpoint unprotected) |
| No authenticated user | DENY — Unauthenticated user. |
| User has no roles | DENY — User has no roles assigned. |
| Endpoint declares no roles | DENY |
| User role not required | DENY — Access denied. Required roles: ... |
| Provider cannot resolve roles | DENY — Role lookup failed. |
| Role lookup throws (DB failure) | DENY — Role lookup failed. |
Public API
| Export | Kind | Purpose |
| --- | --- | --- |
| Authorization | Decorator | @Authorization("ADMIN", ...) or @Authorization({ resource, action }) |
| Allow / Resource | Decorators | Split resource/action metadata (v0.1.0) |
| Action | Enum | CREATE, READ, UPDATE, DELETE, ACTIVATE, DEACTIVATE |
| AuthorizationGuard | Guard | NestJS CanActivate that enforces the metadata |
| AuthorizationEngine | Service | Orchestrates evaluation |
| PolicyEvaluator | Service | Dispatches role vs resource/action policies |
| AuthorizationProvider | Contract | authorize() + optional resolveRoles() |
| AuthorizationContext | Type | User, request, headers, params, metadata |
| AuthorizationPolicy | Type | RoleAuthorizationPolicy \| ResourceAuthorizationPolicy |
| AuthorizationResult | Type | { allowed, reason?, metadata? } |
| AuthorizationModule | Module | forRoot({ provider, global }) |
| AUTHORIZATION_PROVIDER | Token | DI token for the configured provider |
