湯本 開 6a8cfd5530 Merged PR 94: [Sp8-2で絶対着手] 認証・認可を宣言的に扱える仕組みの実装
## 概要
[Task1725: [Sp8-2で絶対着手] 認証・認可を宣言的に扱える仕組みの実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1725)

- 認証(アクセストークンが正しいか)の認証を `@UseGuards(AuthGuard)` をControllerに追加することで確認できる仕組みを追加
  - 実際の修正は別Task想定
- 権限チェック(アクセストークンに含まれる権限でAPI呼び出し可能か)のチェックを `@UseGuards(RoleGuards.configure({ ... }))` をControllerに追加することで確認できる仕組みを追加
  - 実際の修正は別Task想定
- 具体的な使い方はテスト、あるいはUsersControllerのGET /usersのコメントアウトされたコード参照
- 無駄に重複していたコードを共通化

## レビューポイント
- 使いやすそうか?
- この認証Guardsを使用して認証する時の懸念点はないか
- コードに問題はなさそうか
- テスト容易性のため、公開するべきでないメソッドを公開している事に対して納得できるか

## 動作確認状況
- ローカルで確認
2023-05-22 08:08:02 +00:00

113 lines
2.9 KiB
TypeScript

import {
Body,
Controller,
HttpException,
HttpStatus,
Post,
Req,
} from '@nestjs/common';
import {
ApiResponse,
ApiOperation,
ApiBearerAuth,
ApiTags,
} from '@nestjs/swagger';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { ErrorResponse } from '../../common/error/types/types';
import { AuthService } from './auth.service';
import {
AccessTokenResponse,
TokenRequest,
TokenResponse,
} from './types/types';
import { retrieveAuthorizationToken } from '../../common/http/helper';
@ApiTags('auth')
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('token')
@ApiResponse({
status: HttpStatus.OK,
type: TokenResponse,
description: '成功時のレスポンス',
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: '認証エラー',
type: ErrorResponse,
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: '想定外のサーバーエラー',
type: ErrorResponse,
})
@ApiOperation({
description:
'AzureADB2Cでのサインイン後に払いだされるIDトークンを元に認証用のアクセストークンとリフレッシュトークンを生成します',
operationId: 'token',
})
async token(@Body() body: TokenRequest): Promise<TokenResponse> {
console.log(body);
const idToken = await this.authService.getVerifiedIdToken(body.idToken);
const isVerified = await this.authService.isVerifiedUser(idToken);
if (!isVerified) {
throw new HttpException(
makeErrorResponse('E010201'),
HttpStatus.BAD_REQUEST,
);
}
const refreshToken = await this.authService.generateRefreshToken(
idToken,
body.type,
);
const accessToken = await this.authService.generateAccessToken(
refreshToken,
);
return {
accessToken,
refreshToken,
};
}
@Post('accessToken')
@ApiBearerAuth()
@ApiResponse({
status: HttpStatus.OK,
type: AccessTokenResponse,
description: '成功時のレスポンス',
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: '認証エラー',
type: ErrorResponse,
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: '想定外のサーバーエラー',
type: ErrorResponse,
})
@ApiOperation({
operationId: 'accessToken',
description: 'リフレッシュトークンを元にアクセストークンを再生成します',
})
async accessToken(@Req() req): Promise<AccessTokenResponse> {
const refreshToken = retrieveAuthorizationToken(req);
if (refreshToken !== undefined) {
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.UNAUTHORIZED,
);
}
const accessToken = await this.authService.generateAccessToken(
refreshToken,
);
return { accessToken };
}
}