Nest.jsで認証のライブラリであるPassportを使用する方法は公式ドキュメントに載っており、GraphQLでの実装も参考程度に乗っています。
しかし、GraphQLでの実装はあくまでも参考程度なので、これだけでは動作せることができません。
今回は実際にGraphQLでusernameとpasswordでの認証ができるところまで実装してみます。
1. ベースプロジェクトの作成
下記コマンドでNest.jsのプロジェクトを作成します。
$ npm i -g @nestjs/cli
$ nest new passport-sample
$ cd passport-sample
プロジェクト作成後、Authentication requirementsの手順に沿って、必要なライブラリをインストールします。
2. モジュールの作成
Implementing Passport strategies に沿って、passportに必要なサンプルのモジュールの作成と各種ロジックをサンプル実装をします。
ここでは下記のファイルが作成されます。
- users/users.service.ts
- users/users.module.ts
- auth/auth.service.ts
- auth/auth.module.ts
3. LocalStrategyの作成
認証のためのバリデーションを実行するLocalStrategyを[Implementing Passport local] (https://docs.nestjs.com/security/authentication#implementing-passport-local)に沿って実装します。
LocalStrategyは下記の様になります。
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
手順に沿ってAuthModule
も更新します。
4. LocalAuthGuardの作成
公式ドキュメントでは、LocalAuthGuardは下記の様になっています。
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
しかし、この実装だとGraphQLから渡されるパラメーターを正しくLocalStrategyに渡せていないため、認証に失敗してしまいます。
そのため、GraphQLでこのLocalAuthGuardを実装するには、以下のようにgetRequest
メソッドでパラメーターをマッピングしてあげる必要があります。
import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { GqlExecutionContext } from '@nestjs/graphql';
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const gqlReq = ctx.getContext().req;
if (gqlReq) {
gqlReq.body = ctx.getArgs();
return gqlReq;
}
return context.switchToHttp().getRequest();
}
}
5. MutaionにLocalAuthGuardをセットする
まずはUser情報を返却するためのModelを定義します。
import { Field, Int, ObjectType } from '@nestjs/graphql';
@ObjectType()
export class User {
@Field(type => Int)
userId: number;
@Field()
username: string;
}
app.resolver.ts
にloginのMutationを作成し、LocalAuthGuardをセットします。
import { UseGuards, UseInterceptors } from '@nestjs/common';
import { Resolver, Mutation, Args } from '@nestjs/graphql';
import { LocalAuthGuard } from './auth/local-auth.guard';
import { User } from './users/models/user.model';
@Resolver()
export class AppResolver {
@UseGuards(LocalAuthGuard)
@Mutation(() => User)
async login(
@Request() req,
@Args({ name: 'username', type: () => Int }) _: number,
@Args({ name: 'password' }) _: string
): Promise<User> {
return req.user
}
}
これで、GraphQlでPassportを使った認証処理が動く様になります。