@5stones/nest-oidc 中文文档教程

发布于 3年前 浏览 26 项目主页 更新于 3年前

NestJS OIDC

用于 NestJS 和 GraphQL 或 REST 的可配置 OIDC 库。

Install

安装 nest-oidc

npm i @5stones/nest-oidc

安装它的对等依赖

npm i @nestjs/jwt @nestjs/passport passport passport-jwt

项::警告:注意:如果您将此包用于 GraphQL,则需要 安装相应的 GQL 库。 请先遵循 NestJS 文档。

Basic Setup & Usage

您需要导入和配置 AuthModule在你的应用程序中。 这 包包含一个 JWT 身份验证策略,它将验证 JWT 发行人的公钥。 您必须为 oidcAuthority 配置一个值。

import { Module } from '@nestjs/common';
import { AuthModule } from '@5stones/nest-oidc';

@Module({
  imports: [
    ...
    AuthModule.forRoot({
      oidcAuthority: 'http://iam.app.com/auth/realms/app',
    }),
  ],
})
export class AppModule {}

这将添加 JWT 验证策略,并将验证任何传入的 JWT 针对 OIDC 当局的公钥。

最后,您应该使用提供的守卫来装饰您的端点。

Guards

这个包导出了两个基本

  • JwtAuthGuard - for use in REST contexts.
  • JwtAuthGuardGraphQL - for use in GQL contexts.

REST Guard

的守卫:应用守卫需要传递一个有效的 JWT 才能访问任何 控制器端点的:

import { Controller, Get, UseGuards } from '@nestjs/common';
import { Roles, JwtAuthGuard } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuard)
@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

您也可以在特定端点上使用它:

import { Controller, Get, Post, UseGuards } from '@nestjs/common';
import { Roles, JwtAuthGuard } from '@5stones/nest-oidc';

@Controller('cats')
export class CatsController {
  @UseGuards(JwtAuthGuard)
  @Post()
  create(): string {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

GraphQL Guard

应用守卫将需要传递有效的 JWT 才能访问任何 控制器端点的:

import { UseGuards } from '@nestjs/common';
import { Resolver } from '@nestjs/graphql';
import { JwtAuthGuardGraphQL } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuardGraphQL)
@Resolver(() => Cat)
export class CatResolver {
  ...
}

你也可以在特定的端点上使用它:

import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation } from '@nestjs/graphql';
import { JwtAuthGuardGraphQL } from '@5stones/nest-oidc';

@Resolver(() => Cat)
export class CatResolver {

  @Query(() => [Cat], { name: 'allCats' })
  async findAll() {
    ...
  }

  @UseGuards(JwtAuthGuardGraphQL)
  @Mutation(() => Cat, { name: 'createCat' })
  async create() {
    ...
  }
}

Current User

这个包导出两个基本的用户装饰器:

  • CurrentUser - for use in REST contexts.
  • CurrentUserGraphQL - for use in GQL contexts.

REST User

import { Controller, Get, UseGuards } from '@nestjs/common';
import { Roles, JwtAuthGuard, CurrentUser } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuard)
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@CurrentUser() user: any): string {
    return 'This action returns all cats';
  }
}

GraphQL User

import { UseGuards } from '@nestjs/common';
import { Resolver, Query } from '@nestjs/graphql';
import { JwtAuthGuardGraphQL, CurrentUserGraphQL } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuardGraphQL)
@Resolver(() => Cat)
export class CatResolver {

  @Query(() => [Cat], { name: 'allCats' })
  async findAll(@CurrentUserGraphQL() user: any) {
    ...
  }
}

Roles

如果你想根据 JWT 的属性许可不同的端点,你 可以将 Roles 装饰器与 Auth Guards 结合使用(两者都是 休息与 GQL)。 Roles 装饰器将接受一个 string 列表并将 检查访问该端点的用户对象是否在 user.roles 属性。 它期望 user.roles 属性是一个平面数组 字符串。

Example

import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql';
import { JwtAuthGuardGraphQL, Roles } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuardGraphQL)
@Resolver(() => Cat)
export class CatResolver {

  @Query(() => [Cat], { name: 'allCats' })
  async findAll(): Promise<Cat[]> {
    ...
  }

  @Roles()
  @Query(() => Cat, { name: 'Cat' })
  async findOne(@Args('id', { type: () => ID }) id: number): Promise<Cat> {
    ...
  }

  @Roles('ADMIN', 'SUPER_ADMIN')
  @Mutation(() => Cat, { name: 'createCat' })
  async create(): Promise<Cat> {
    ...
  }
}

在这种情况下,突变只能由 ADMINSUPER_ADMIN 执行 但查询可以由任何拥有有效 JWT 的用户执行。

:warning: 注意:如果您没有将 any 角色参数传递给 Roles 装饰器(即 @Roles()),这与根本不添加装饰器是一样的。 也就是说,findAllfindOne 查询之上的任何场景 行为相同。

Role Evaluators

如果您的 JWT 本身没有字符串的 .roles 属性,您可以 使用评估器将 JWT 的属性映射到角色。 你可以这样做 配置 roleEvaluatorsroleEvaluators 是一个数组 RoleEvaluator 对象,包含一个expression,以及访问role 该特定表达式在评估为 true 时授予。

expression 可以是任何有效的 jexl 表达。

Example

假设您有一个具有以下结构的 JWT:

{
  roles: [
     { name: "SUPER_USER", id: 1 },
     ...
     { name: "PREMIUM", id: 2 },
  ],
}

然后您可以像下面这样配置一个评估器,它将映射一个 具有 角色 且名称为 SUPER_USER 的用户到 ADMIN 在您的应用程序中的作用。

import { Module } from '@nestjs/common';
import { AuthModule } from '@5stones/nest-oidc';

@Module({
  imports: [
    ...
    AuthModule.forRoot({
      oidcAuthority: 'http://iam.app.com/auth/realms/app',
      roleEvaluators: [
        {
          expression: 'jwt.roles[.name == "SUPER_USER"]|length > 0',
          role: 'ADMIN',
        },
      ],
    }),
  ],
})
export class AppModule {}

您的应用程序中的用户对象现在将具有以下内容:

{
  ...
  roles: [
    "ADMIN",
  ],
}

然后您只需使用 @Roles('ADMIN') 装饰您的端点 注释以便将其锁定到该角色的用户。

JWT Mapper

默认情况下,JWT 负载作为用户传递到应用程序中。 然而, 如果您需要将 JWT 负载映射到不同的结构,您可以通过 jwtMapper 选项:

import { Module } from '@nestjs/common';
import { AuthModule } from '@5stones/nest-oidc';

@Module({
  imports: [
    ...
    AuthModule.forRoot({
      oidcAuthority: 'http://iam.app.com/auth/realms/app',
      jwtMapper: async (payload: any) => ({
        id: payload.sub,
        email: payload.email,
        ...
      }),
    }),
  ],
})
export class AppModule {}

Release

这个项目的标准发布命令是:

npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]

这个命令将:

  1. Generate/update the Changelog
  2. Bump the package version
  3. Tag & pushing the commit

例如

npm version 1.2.17
npm version patch // 1.2.17 -> 1.2.18

NestJS OIDC

A configurable OIDC library for NestJS and GraphQL or REST.

Install

Install nest-oidc:

npm i @5stones/nest-oidc

Install it's peer dependencies:

npm i @nestjs/jwt @nestjs/passport passport passport-jwt

:warning: Note: If you're using this package for GraphQL you'll need the corresponding GQL libraries installed. Please follow the NestJS documentation first.

Basic Setup & Usage

You'll need to import and configure the AuthModule in your application. This package contains a JWT authentication strategy which will validate a JWT against the issuer's public key. You must pass configure a value for the oidcAuthority.

import { Module } from '@nestjs/common';
import { AuthModule } from '@5stones/nest-oidc';

@Module({
  imports: [
    ...
    AuthModule.forRoot({
      oidcAuthority: 'http://iam.app.com/auth/realms/app',
    }),
  ],
})
export class AppModule {}

This will add the JWT validation strategy, and will verify any incoming JWT's against the OIDC authorities public keys.

Finally, you should decorate your endpoints with the provided guards.

Guards

This package exports two basic guards:

  • JwtAuthGuard - for use in REST contexts.
  • JwtAuthGuardGraphQL - for use in GQL contexts.

REST Guard

Applying the guard will require a valid JWT to be passed in order to access any of the controller endpoints:

import { Controller, Get, UseGuards } from '@nestjs/common';
import { Roles, JwtAuthGuard } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuard)
@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

You can also use it on specific endpoints:

import { Controller, Get, Post, UseGuards } from '@nestjs/common';
import { Roles, JwtAuthGuard } from '@5stones/nest-oidc';

@Controller('cats')
export class CatsController {
  @UseGuards(JwtAuthGuard)
  @Post()
  create(): string {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

GraphQL Guard

Applying the guard will require a valid JWT to be passed in order to access any of the controller endpoints:

import { UseGuards } from '@nestjs/common';
import { Resolver } from '@nestjs/graphql';
import { JwtAuthGuardGraphQL } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuardGraphQL)
@Resolver(() => Cat)
export class CatResolver {
  ...
}

You can also use it on specific endpoints:

import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation } from '@nestjs/graphql';
import { JwtAuthGuardGraphQL } from '@5stones/nest-oidc';

@Resolver(() => Cat)
export class CatResolver {

  @Query(() => [Cat], { name: 'allCats' })
  async findAll() {
    ...
  }

  @UseGuards(JwtAuthGuardGraphQL)
  @Mutation(() => Cat, { name: 'createCat' })
  async create() {
    ...
  }
}

Current User

This package exports two basic user decorators:

  • CurrentUser - for use in REST contexts.
  • CurrentUserGraphQL - for use in GQL contexts.

REST User

import { Controller, Get, UseGuards } from '@nestjs/common';
import { Roles, JwtAuthGuard, CurrentUser } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuard)
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@CurrentUser() user: any): string {
    return 'This action returns all cats';
  }
}

GraphQL User

import { UseGuards } from '@nestjs/common';
import { Resolver, Query } from '@nestjs/graphql';
import { JwtAuthGuardGraphQL, CurrentUserGraphQL } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuardGraphQL)
@Resolver(() => Cat)
export class CatResolver {

  @Query(() => [Cat], { name: 'allCats' })
  async findAll(@CurrentUserGraphQL() user: any) {
    ...
  }
}

Roles

If you want to permission different endpoints based on properties of the JWT you can do so using the Roles decorator in conjunction with the Auth Guards (both REST & GQL). The Roles decorator will accept a list of strings and will check if the user object accessing that endpoint has any of those strings in the user.roles property. It expects the user.roles property to be a flat array of strings.

Example

import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql';
import { JwtAuthGuardGraphQL, Roles } from '@5stones/nest-oidc';

@UseGuards(JwtAuthGuardGraphQL)
@Resolver(() => Cat)
export class CatResolver {

  @Query(() => [Cat], { name: 'allCats' })
  async findAll(): Promise<Cat[]> {
    ...
  }

  @Roles()
  @Query(() => Cat, { name: 'Cat' })
  async findOne(@Args('id', { type: () => ID }) id: number): Promise<Cat> {
    ...
  }

  @Roles('ADMIN', 'SUPER_ADMIN')
  @Mutation(() => Cat, { name: 'createCat' })
  async create(): Promise<Cat> {
    ...
  }
}

In this scenario, the mutation can only be executed by an ADMIN or SUPER_ADMIN but the query can be executed by any user with a valid JWT.

:warning: Note: if you do not pass any roles parameters to the Roles decorator (i.e. @Roles()) it is the same as not adding the decorator at all. That is to say, any scenario above the findAll and the findOne queries behave identically.

Role Evaluators

If your JWT doesn't natively have a .roles property of strings on it, you can use evaluators to map properties of the JWT to a role. You can do so by configuring roleEvaluators. roleEvaluators are an array of RoleEvaluator objects which consist of an expression, and the access role that that particular expression grants upon evaluating to true.

An expression can be any valid jexl expression.

Example

Suppose you have a JWT with the following structure:

{
  roles: [
     { name: "SUPER_USER", id: 1 },
     ...
     { name: "PREMIUM", id: 2 },
  ],
}

You could then configure an evaluator like the following, which would map a user that has a role of with the name of SUPER_USER to the ADMIN role in your application.

import { Module } from '@nestjs/common';
import { AuthModule } from '@5stones/nest-oidc';

@Module({
  imports: [
    ...
    AuthModule.forRoot({
      oidcAuthority: 'http://iam.app.com/auth/realms/app',
      roleEvaluators: [
        {
          expression: 'jwt.roles[.name == "SUPER_USER"]|length > 0',
          role: 'ADMIN',
        },
      ],
    }),
  ],
})
export class AppModule {}

The user object within your application will now have the following:

{
  ...
  roles: [
    "ADMIN",
  ],
}

Then you would simply decorate your endpoint with the @Roles('ADMIN') annotation in order to lock it down to users of that role.

JWT Mapper

By default, the JWT payload is passed as the user into the application. However, if you need to map the JWT payload to different structure you can pass the jwtMapper option:

import { Module } from '@nestjs/common';
import { AuthModule } from '@5stones/nest-oidc';

@Module({
  imports: [
    ...
    AuthModule.forRoot({
      oidcAuthority: 'http://iam.app.com/auth/realms/app',
      jwtMapper: async (payload: any) => ({
        id: payload.sub,
        email: payload.email,
        ...
      }),
    }),
  ],
})
export class AppModule {}

Release

The standard release command for this project is:

npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]

This command will:

  1. Generate/update the Changelog
  2. Bump the package version
  3. Tag & pushing the commit

e.g.

npm version 1.2.17
npm version patch // 1.2.17 -> 1.2.18
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文