LoginSignup
2

More than 1 year has passed since last update.

Nest.jsでGraphQL + Passportを使って認証処理を作る。

Posted at

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に沿って実装します。
LocalStrategyは下記の様になります。

auth/local.strategy.ts
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は下記の様になっています。

auth/local-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}

しかし、この実装だとGraphQLから渡されるパラメーターを正しくLocalStrategyに渡せていないため、認証に失敗してしまいます。
そのため、GraphQLでこのLocalAuthGuardを実装するには、以下のようにgetRequestメソッドでパラメーターをマッピングしてあげる必要があります。

auth/local-auth.guard.ts
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を定義します。

users/models/user.model.ts

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をセットします。

app.resolver.ts
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を使った認証処理が動く様になります。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2