1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[NestJS]JwtStrategyでサービスをインジェクション出来なくてハマった時のメモ

Posted at

以前に、NestJSのバージョン6くらいの時にできてたことが最新のバージョン(ver9系)だとできなくてかなりの時間を取られたので、解決方法を備忘録として残しておきます。

バージョン

$ nest --version
9.1.3

環境構築

プロジェクト作成

nest new jwt-test
cd jwt-test

JWT認証関連のパッケージインストール

リファレンスに従ってJWTに必要なパッケージをインストール

npm install --save @nestjs/passport passport
npm install --save @nestjs/jwt passport-jwt
npm install --save-dev @types/passport-jwt

※passport-localは今回使わないので、インストールしません

JwtStrategyの作成

まずはコンストラクタに何も指定しない状態で試します

src/auth/jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'secret',
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

JwtStrategyを使う

src/auth/auth.controller.ts
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { JwtAuthGuard } from './jwt-auth.guard';

@Controller({ path: 'auth' })
export class AuthController {
  constructor(private authService: AuthService) {}

  @Post('login')
  login(@Body() body: { username: string; password: string }) {
    return this.authService.login(body);
  }

  /**
   * 認証が必要な処理
   * @returns
   */
  @UseGuards(JwtAuthGuard)
  @Get('test')
  test() {
    return '認証成功';
  }
}

動作確認してみる

Talend API Testerで動確してみました

  • /auth/testをトークンなしで呼び出すと401

image.png

  • /auth/loginでJWTを生成
  • /auth/testAuthorization: Bearer {JWT}をヘッダに付与してもう一度呼び出します

image.png

本題:JwtStrategyで追加のチェックをしたい

JWTの認証が通った時に、ユーザーの持つ権限をDBから取得しようとしたんですが、ここでハマりました。。。

以前は以下のように直接インジェクション出来てたんですが、最新版ではできなくなっていました。

src/auth/jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { AuthService } from 'src/auth/auth.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    // 直接インジェクション
  constructor(private authService: AuthService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'secret',
    });
  }

  async validate(payload: any) {
    const userId = payload.sub;

    // AuthServiceのメソッド呼び出し
    const user = await authService.validate(userId);
    return {
      userId: user.id,
      username: user.username,
      authorities: user.authorities,
    };
  }
}

これでUnknown authentication strategy "jwt"エラーとなりました。。。

解決法:moduleRefを使う

リファレンスにちっちゃく書いてありました

src/auth/auth.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { ContextIdFactory, ModuleRef } from '@nestjs/core';
import { Request } from 'express';
import { AuthService } from './auth.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  // ModuleRefをインジェクション
  constructor(private moduleRef: ModuleRef) {
    super({
      // こいつを指定すると、validateの第一引数にRequestが渡されるようになる
      passReqToCallback: true,
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'secret',
    });
  }

  async validate(request: Request, payload: any) {
    // サービスを取り出す(JwtStrategyでprovideしているサービスしか指定できない?)
    const contextId = ContextIdFactory.getByRequest(request);
    const authService = await this.moduleRef.resolve(AuthService, contextId);

    const userId = payload.sub;

    // UsersServiceのメソッド呼び出し
    const user = await authService.validate(userId);
    return {
      userId: user.id,
      username: user.username,
      authorities: user.authorities,
    };
  }
}

説明を読む感じ、RequestScopeを使ってるとコンストラクタで直接インジェクションが出来ないってことですかね?

めちゃくちゃハマりましたが、無事に解決できてよかった。。。

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?