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

@svton/nestjs-authz

v1.2.2

Published

NestJS authorization module with role and permission guards

Readme

@svton/nestjs-authz

NestJS 授权模块,提供:

  • @Roles() 角色校验
  • @Permissions() 权限校验
  • 基于 @svton/authz 的角色继承和作用域授权
  • 与现有 req.user.role / req.user.roles 兼容的轻量接入
  • AuthzGuard / RolesGuard 双名称导出

安装

pnpm add @svton/nestjs-authz

使用

模块注册

import { Module } from '@nestjs/common';
import { AuthzModule } from '@svton/nestjs-authz';

@Module({
  imports: [
    AuthzModule.forRoot({
      userRoleField: 'roles', // 从 req.user.roles 读取角色
      userPermissionsField: 'permissions', // 从 req.user.permissions 读取直接权限
      enableGlobalGuard: true, // 全局启用 RolesGuard
      allowNoRoles: true, // 没有设置角色要求时放行
      schema: {
        roles: {
          admin: {
            permissions: ['*'],
          },
          manager: {
            permissions: ['users:read', 'users:update'],
          },
          team_member: {
            permissions: [{ resource: 'team', action: 'read', scopeTypes: ['team'] }],
          },
          team_admin: {
            inherits: ['team_member'],
            permissions: [{ resource: 'member', action: 'invite', scopeTypes: ['team'] }],
          },
        },
      },
      getScope: (context) => {
        const request = context.switchToHttp().getRequest();
        const teamId = request.params?.teamId;
        return teamId ? { type: 'team', id: teamId } : undefined;
      },
    }),
  ],
})
export class AppModule {}

getAssignments() 和 getScope() 都支持返回 Promise,适合按请求查库后再决定 scoped role:

AuthzModule.forRootAsync({
  imports: [PrismaModule],
  inject: [PrismaService],
  useFactory: (prisma: PrismaService) => ({
    schema: {
      roles: {
        team_member: {},
        team_admin: {
          inherits: ['team_member'],
        },
      },
    },
    getAssignments: async (context) => {
      const request = context.switchToHttp().getRequest();
      const teamId = request.headers['x-team-id'];
      const membership = await prisma.teamMember.findUnique({
        where: {
          teamId_userId: { teamId, userId: request.user.id },
        },
      });

      if (!membership) {
        return {};
      }

      return {
        roles: [
          {
            role: membership.role === 'admin' ? 'team_admin' : 'team_member',
            scope: { type: 'team', id: teamId },
          },
        ],
      };
    },
  }),
});

使用装饰器

import { Controller, Delete, Get, Param, UseGuards } from '@nestjs/common';
import { AuthzGuard, Permissions, Public, Roles } from '@svton/nestjs-authz';
import { JwtAuthGuard } from './auth/jwt-auth.guard';

@Controller('users')
@UseGuards(JwtAuthGuard, AuthzGuard) // 如果没有全局启用
export class UserController {
  // 需要 admin 或 moderator 角色
  @Roles('admin', 'moderator')
  @Get()
  findAll() {
    return [];
  }

  // 需要 users:read 权限
  @Permissions('users:read')
  @Get('report')
  report() {
    return [];
  }

  // 只需要 admin 角色
  @Roles('admin')
  @Delete(':id')
  remove(@Param('id') id: string) {
    return { deleted: id };
  }

  // 公开路由,跳过角色检查
  @Public()
  @Get('public')
  publicEndpoint() {
    return { message: 'This is public' };
  }
}

与 JWT 配合使用

确保 JWT payload 中包含角色或权限信息:

// auth.service.ts
@Injectable()
export class AuthService {
  async login(user: User) {
    const payload = {
      sub: user.id,
      email: user.email,
      roles: user.roles, // ['admin'] | ['manager']
      permissions: user.permissions, // ['users:read']
    };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

// jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  async validate(payload: JwtPayload) {
    return {
      id: payload.sub,
      email: payload.email,
      roles: payload.roles,
      permissions: payload.permissions,
    };
  }
}

配置选项

| 选项 | 类型 | 默认值 | 说明 | |------|------|--------|------| | userRoleField | string | 'role' | 用户角色字段(支持嵌套如 'profile.role') | | userPermissionsField | string | 'permissions' | 用户直接权限字段 | | enableGlobalGuard | boolean | false | 是否全局启用 RolesGuard | | allowNoRoles | boolean | true | 没有设置角色要求时是否放行 | | schema | AuthzSchema | - | 角色继承与权限模型 | | getAssignments | function | - | 自定义解析角色/权限,支持异步 | | getScope | function | - | 为当前请求解析授权 scope,支持异步 |

默认 userRoleField 是 role。如果未显式配置且 req.user.role 不存在,守卫也会自动尝试读取 req.user.roles。

forRootAsync() 与全局 Guard

异步注册时也支持在工厂返回值里设置 enableGlobalGuard: true,模块会自动将授权守卫接到 APP_GUARD。

多角色支持

如果用户有多个角色(数组形式),只要有一个角色匹配即可通过:

// JWT payload
{
  sub: '123',
  roles: ['user', 'editor'] // 数组形式
}

// 配置
AuthzModule.forRoot({
  userRoleField: 'roles', // 指向数组字段
})

// 使用
@Roles('editor', 'admin') // 用户有 editor 角色,可以访问
@Get('articles')
findAll() {}

权限校验

@Permissions() 支持字符串权限和 { resource, action } 两种形式。传入多个权限时,命中任一项即可通过:

@Permissions('users:read', { resource: 'users', action: 'export' })
@Get('export')
exportUsers() {}

作用域授权

当角色或权限是作用域化的,比如某个用户仅是 team_1 的 team_admin,可以通过 getScope() 将当前请求绑定到具体作用域:

AuthzModule.forRoot({
  schema: {
    roles: {
      team_admin: {
        permissions: [{ resource: 'member', action: 'invite', scopeTypes: ['team'] }],
      },
    },
  },
  getScope: (context) => {
    const request = context.switchToHttp().getRequest();
    return { type: 'team', id: request.params.teamId };
  },
});

@Permissions({ resource: 'member', action: 'invite' })
@Post('teams/:teamId/members')
inviteMember() {}

动态权限

已支持:用户的角色/权限实时来自数据库

通过 getAssignments() 在每个请求里查库,用户被授予/撤销角色或权限时立即生效,无需重启服务。RolesGuard 每次请求都会重新构建授权器,没有跨请求的缓存问题。

AuthzModule.forRootAsync({
  inject: [PrismaService],
  useFactory: (prisma: PrismaService) => ({
    schema: { /* 静态角色定义 */ },
    getAssignments: async (context) => {
      const request = context.switchToHttp().getRequest();
      // 每个请求实时查库,DB 改了立刻生效
      const userRoles = await prisma.userRole.findMany({
        where: { userId: request.user.id },
      });
      return {
        roles: userRoles.map((r) => ({
          role: r.role,
          scope: r.teamId ? { type: 'team', id: r.teamId } : undefined,
        })),
      };
    },
  }),
});

不支持(也不建议支持):schema 热更新

角色定义(schema.roles)应作为业务建模的一部分,跟随代码版本化、review、灰度发布。让运营人员在后台动态创建角色定义会引入难以回滚的风险——一旦误删某个关键角色的权限,整个系统可能挂掉。

如果确实需要"角色名由运营自定义",常见做法是:

  • schema 中预留通配角色(如 custom:*)
  • 在 getAssignments() 里把 DB 中的自定义角色名映射到 schema 中的固定角色

更多能力边界说明参见 @svton/authz README。