Nest.jsのGuardでCognito UserPoolsのユーザー認証をする
Nest.jsのGuardを使うことで、APIへのアクセス許可を管理できます。 AWSのCognito UserPoolsでユーザーを管理していることが多いので、そのためのGuardを簡単に作ってみました。 コード 実装 […]
目次
Nest.jsのGuardを使うことで、APIへのアクセス許可を管理できます。
AWSのCognito UserPoolsでユーザーを管理していることが多いので、そのためのGuardを簡単に作ってみました。
コード
実装は2ファイル + アルファです。Nest CLIでサクッと作りましょう。
% nest g s cognito
CREATE /src/cognito/cognito.service.spec.ts (467 bytes)
CREATE /src/cognito/cognito.service.ts (91 bytes)
UPDATE /src/app.module.ts (1046 bytes)
% nest g gu authorizer
CREATE /src/authorizer.guard.spec.ts (184 bytes)
CREATE /src/authorizer.guard.ts (305 bytes)
ファイル1: AWS SDKでCognitoにアクセスするサービス
まずは/src/cognito/cognito.service.ts
に実装を書きます。
import { Injectable } from '@nestjs/common';
import { CognitoIdentityServiceProvider } from 'aws-sdk';
import { GetUserResponse } from 'aws-sdk/clients/cognitoidentityserviceprovider';
@Injectable()
export class CognitoService {
private client: CognitoIdentityServiceProvider
protected user: GetUserResponse
constructor() {
this.client = new CognitoIdentityServiceProvider()
}
public async getUserByToken(token: string): Promise<GetUserResponse> {
this.user = await this.client.getUser({
AccessToken: token
}).promise()
return this.user
}
public loadCurrentUser(): GetUserResponse {
return this.user
}
}
getUserByToken
でリクエストヘッダーのtokenを使ってユーザー取得を試行します。Controllerなどでユーザー情報を取得する場合はloadCurrentUser
を使います。
ファイル2: Gurdでのアクセス管理
もう1つのファイル、/src/authorizer.guard.ts
が実際にアクセスの制御を行うファイルです。
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { Request } from 'express';
import { CognitoService } from './cognito.service';
@Injectable()
export class AuthorizerGuard implements CanActivate {
constructor(private readonly cognito: CognitoService) {}
async canActivate(
context: ExecutionContext,
): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request>();
const { authorization } = request.headers;
await this.authorizeByCognito(authorization)
return true;
}
public async authorizeByCognito(authorizationToken?: string):Promise<void> {
if (!authorizationToken) throw new UnauthorizedException(`Authorization header is required.`)
try {
await this.cognito.getUserByToken(authorizationToken)
} catch (e) {
if (e.name === 'NotAuthorizedException') throw new UnauthorizedException()
throw e
}
}
}
リクエストヘッダーにauthorization
がない場合や、Conitoからユーザーが取得できなかった場合にUnauthorizedException
を投げるようにしています。
エラーが投げられなければ問題なしとして、最後にreturn trueする実装としました。
authorizeByCognito
メソッドをわざわざ作らなくても問題はないのですが、canActivateメソッドに対してテストを書くのがちょっと煩雑だったために分割しています。
APIへの設定とユーザーの取得
あとはAPIへの設定と、その後ユーザーの情報を取得する部分です。
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthorizerGuard } from './aws/cognito/authorizer.guard';
import { CognitoService } from './aws/cognito/cognito.service';
@Controller()
@UseGuards(AuthorizerGuard)
export class AppController {
constructor(private readonly cognito: CognitoService) {}
@Get()
getHello(): string {
return `Hello ${this.cognito.loadCurrentUser().Username}`;
}
}
useGuards
デコレーターで作成したGuardを設定し、CognitoService
を使ってユーザー名などをロードしています。