【NestJS x Auth0】NestJSで作成したAPIにAuth0の認証/認可を追加する 〜認可(NestJS)編〜
前回の続きです。
- NestJSで作成したAPIにAuth0の認証/認可を追加する 〜準備編〜
- NestJSで作成したAPIにAuth0の認証/認可を追加する 〜準備編 その2〜
- NestJSで作成したAPIにAuth0の認証/認可を追加する 〜認証編〜
- NestJSで作成したAPIにAuth0の認証/認可を追加する 〜認可(Auth0)編〜
前回はAuth0側でRBACの設定をしたので、
今回はNestJS側でRBACを実装していきます。
引き続き以下の記事を参考にしています。
1.カスタムデコレーター(@Permissions)の作成
NestJSでRBACを実現するには認証のときと同じようにNestJSのGuard
機能を使い、PermissionGuard
を作成します。
PermissionGuard
では、APIの実行に必要なPermissionがアクセストークン内のpermissions
に含まれているかどうか確認したいのですが、
APIごとに必要なPermissionは異なるためPermissionGuard
を作成する前にカスタムデコレータ(@Permissions
)を作成して必要なPermissionをPermissionGuard
に渡せるようにします。
@HogeHoge
をデコレーターと呼びます。
使用イメージ
@UseGuards
でPermissionGuard
を指定し、必要なPermissionを@Permissions
で指定します。
@UseGuards(AuthGuard('jwt'), PermissionsGuard) @Post() @Permissions('create:items') create(@Body('item') item: Item) { this.itemsService.create(item); }
カスタムデコレーターもリソースなどと同様、Nest CLIを使って作成します。
# --no-spec はテストファイルを生成しないオプションです npx nest generate decorator permissions --no-spec
デコレーターの作成はなんとこれで終わりです。
作成したデコレーターでやっていることは@Permissions('create:items')
で指定した引数('create:items'
)をメタデータと呼ばれるフィールドに格納しているだけです。
メタデータに格納しておくことでGuard
などからその値を参照することができます。
デコレーターを作成しなくても
@SetMetadata()
デコレーターで同様のことができますが公式サイトではデコレーターを実装することが推奨されています。
Guards | NestJS - A progressive Node.js framework
2. PermissionGuardの作成
PermissionGuard
に必要なPermissionを渡すことができるようになったので、PermissionGuard
を作成します。
作成したら、src/permissions/permissions.guard.ts
を以下のように修正します。
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class PermissionsGuard implements CanActivate { constructor(private readonly reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { // メタデータに格納したPermissionを取得 const routePermissions = this.reflector.get<string[]>( 'permissions', context.getHandler(), ); // トークンに含まれるPermissionを取得 const userPermissions = context.switchToHttp().getRequest() .user.permissions; if (!routePermissions) { return true; } // 要求したPermissionがトークンに含まれているかチェック const hasPermission = () => routePermissions.every((routePermission) => userPermissions.includes(routePermission), ); return hasPermission(); } }
これでPermissionGuard
の実装は終了です。
3. PermissionGuardの適用
ItemController
を修正して、各APIにPermissionGuard
を適用します。
また、忘れずに@Permissions
デコレーターも設定します。
export class ItemsController { constructor(private readonly itemsService: ItemsService) {} @Get() findAll() { return this.itemsService.findAll(); } @Get(':id') findOne(@Param('id') id: string) { return this.itemsService.findOne(id); } @Post() @UseGuards(AuthGuard('jwt'), PermissionsGuard) @Permissions('create:items') create(@Body() createItemDto: CreateItemDto) { return this.itemsService.create(createItemDto); } @Patch(':id') @UseGuards(AuthGuard('jwt'), PermissionsGuard) @Permissions('update:items') update(@Param('id') id: string, @Body() updateItemDto: UpdateItemDto) { return this.itemsService.update(id, updateItemDto); } @Delete(':id') @UseGuards(AuthGuard('jwt'), PermissionsGuard) @Permissions('delete:items') remove(@Param('id') id: string) { return this.itemsService.remove(id); } }
動作確認
すべての設定が終わったので、動作確認してみます。
まずは管理者(admin
Role)で実行してすべての操作ができることを確認します。
トークンは前回使ったAuth0サンプルアプリケーションで取得します。
すべてのAPIが実行できたので、利用者(general
Role)ユーザーでやってみます。
削除のときのみ、403 Forbidden
となったので期待通り動いていそうです。
{ "statusCode": 403, "message": "Forbidden resource", "error": "Forbidden" }
ソースコード
まとめ
NestJSとAuth0を連携させて簡単なAPIに認証/認可を実装しました(長かった。。。)。
認証/認可というと難しいイメージがありましたが、参考記事としてあげているAuth0のブログにほとんど書いてあるので比較的簡単に実装できました(といっても結構時間かかりましたが。。。)。
寄り道して知ったことや気がついたことについては、気が向いたらまとめようと思います。