NestJS USER AUTHENTICATION


設定

npm i jsonwebtoken
npm i jsonwebtoken @types/jsonwebtoken --only-dev

使用


ConfigModule.SECRET KEYをforRootのvalidationSchemaに追加します.
SECRET KEYはjwtです.signでprivateKeyを使う
app.module.ts
import { Module } from '@nestjs/common';
import * as Joi from 'joi';
import { ConfigModule } from '@nestjs/config';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { CommonModule } from './common/common.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, //우리 어플리케이션의 어디서나 config 모듈에 접근할 수 있다는 것
      envFilePath: process.env.NODE_ENV === 'dev' ? '.env.dev' : '.env.test',
      ignoreEnvFile: process.env.NODE_ENV === 'prod', //서버에 deply 할 때 환경변수 파일을 사용하지 않는다는 것
      validationSchema: Joi.object({
        NODE_ENV: Joi.string().valid('dev', 'prod').required(),
        DB_HOST: Joi.string().required(),
        DB_PROT: Joi.string().required(),
        DB_USERNAME: Joi.string().required(),
        DB_PASSWORD: Joi.string().required(),
        DB_NAME: Joi.string().required(),
        SECRET_KEY: Joi.string().required(),
      }),
    }),
    TypeOrmModule.forRoot({
      ...
    }),
    GraphQLModule.forRoot({
      autoSchemaFile: true, //메모리에 저장
    }),
    UsersModule,
    CommonModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}
SECRET KEYを処理します.env.SECRET KEYとしてインポートすることもできますが、configServiceでインポートすることもできます.
元のユーザー.modules.tsにconfigサービスを提供する必要がありますが、モジュールを構成するオプションはisGlobal:trueなので、プロバイダを必要とせずに使用できます.
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UsesrResolver } from './users.resolver';
import { UsersService } from './users.service';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  providers: [UsesrResolver, UsersService],
})
export class UsersModule {}
users.service.ts
private readonly config:ConfigServiceを宣言して構成します.変数名をget()メソッドのパラメータに渡すことで、環境変数をインポートできます.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as jwt from 'jsonwebtoken';
import { CreateAccountInput } from './dtos/create-account.dto';
import { LoginInput } from './dtos/login.dto';
import { User } from './entities/user.entity';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly users: Repository<User>,
    private readonly config: ConfigService,
  ) {}

  //create User
  ...

  //Login User
  async login({
    email,
    password,
  }: LoginInput): Promise<{ ok: boolean; error?: string; token?: string }> {
    // make a JMT and give it to the user
    try {
      // find ths user with the email
      const user = await this.users.findOne({ email });
      if (!user) {
        return { ok: false, error: 'User not found' };
      }
      // check if the passowrd is correct
      const passwordCorrect = await user.checkPassword(password);
      if (!passwordCorrect) {
        return { ok: false, error: 'Wrong password' };
      }
      const token = jwt.sign({ id: user.id }, this.config.get('SECRET_KEY'));
      return { ok: true, token: 'goododo' };
    } catch (error) {
      return { ok: false, error };
    }
  }
}
jwt.Signの最初のパラメータはtokenにどのような情報を入れるかを指定し、privateKeyは2番目のパラメータとして入ります.
tokenをユーザーに指定すると、ユーザーは自分のtokenに何が含まれているかを表示できます.
これは、ユーザが自分のtokenのパスワードを復号できることを意味する.
したがって、tokenは重要な個人情報を格納するのに適していない.だから私はuserのid値をtokenにあげました
privateKeyを使用してtokenを指定する目的は、ユーザーがtokenの情報を変更する場合です.
privateKeyでユーザーがtokenを変更したかどうかを検証できるからです.
これは,修正後の情報も認識できることを意味する.
nestjsのモジュールは2種類あります
1つ目は静的モジュールです
静的モジュールとは、設定が適用されていないことを意味します.
2つ目はダイナミックモジュールです.
ダイナミックモジュール(Dynamic Module)は、GraphQLModuleと同様に設定されています.
ここで注意すべきは、ダイナミックモジュールが実際には最終的に静的モジュールになることである.
dynamic moduleはまずstatic moduleであり、戻り値はdynamic moduleである.
JWTモジュールの作成
jwtモジュールおよびサービスは、nest g mo jwt、nest g s jwtによって作成される.
JWTモジュールをダイナミックモジュールにするため、jwt.module.tsで:
import { DynamicModule, Global, Module } from '@nestjs/common';
import { JwtService } from './jwt.service';

@Module({})
@Global() // 전역으로 사용가능하게 만들어줍니다. ConfigModule의 isGlobal: true와 같다고 보면됩니다.
export class JwtModule {
	// dynamic module은 일단 static module이고 반환값이 dynamic module입니다.
  static forRoot(): DynamicModule { 
    return {
      module: JwtModule,
      exports: [JwtService],
      providers: [JwtService],
    };
  }
}
jwt.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class JwtService {
  hello() {
    console.log('Hello');
  }
}
app.module.ts
これにより、いつでもどこでも依存性を注入することでJwtServiceを使用できます.
import { Module } from '@nestjs/common';
import * as Joi from 'joi';
import { ConfigModule } from '@nestjs/config';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { CommonModule } from './common/common.module';
import { User } from './users/entities/user.entity';
import { JwtModule } from './jwt/jwt.module';

@Module({
  imports: [
    //...설정

    JwtModule.forRoot(),
    UsersModule,
    CommonModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}
users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as jwt from 'jsonwebtoken';
import { CreateAccountInput } from './dtos/create-account.dto';
import { LoginInput } from './dtos/login.dto';
import { User } from './entities/user.entity';
import { ConfigService } from '@nestjs/config';
import { JwtService } from 'src/jwt/jwt.service';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly users: Repository<User>,
    private readonly config: ConfigService,
    private readonly jwtService: JwtService,
  ) {
    console.log(this.jwtService.hello()); //Hello
  }

  ...
}
console.log.jwtservice.hello出力を見てみましょう.
custom provider
プロバイダ:WTServiceの元の形式は、「提供:WTService、ユーザー・クラス:WTService」です.
ベンダーをカスタマイズできます.
import { DynamicModule, Global, Module } from '@nestjs/common';
import { JwtModuleOptions } from './jwt.interfaces';
import { CONFIG_OPTIONS } from './jwt.constants';
import { JwtService } from './jwt.service';

@Module({})
@Global()
export class JwtModule {
  static forRoot(options: JwtModuleOptions): DynamicModule {
    return {
      module: JwtModule,
      exports: [JwtService],
      providers: [
        {
          //CONFIG_OPTIONS라는 이름을 가진 provider가 있고 그 value가 options인 것이다
          provide: CONFIG_OPTIONS,
          useValue: options,
        },
        JwtService, 
      ],
    };
  }
}
jwt.constants.ts
export const CONFIG_OPTIONS = 'CONFIG_OPTIONS';
jwt.interfaces.ts
export interface JwtModuleOptions {
  privateKey: string;
}
app.module.ts
import { Module } from '@nestjs/common';
import * as Joi from 'joi';
import { ConfigModule } from '@nestjs/config';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { CommonModule } from './common/common.module';
import { User } from './users/entities/user.entity';
import { JwtModule } from './jwt/jwt.module';

@Module({
  imports: [
    //...설정

    JwtModule.forRoot({
      privateKey: process.env.PRIVATE_KEY,
    }),
    UsersModule,
    CommonModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}
jwt.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { JwtModuleOptions } from './jwt.interfaces';
import { CONFIG_OPTIONS } from './jwt.constants';

@Injectable()
export class JwtService {
  constructor(
    @Inject(CONFIG_OPTIONS)
    private readonly options: JwtModuleOptions,
  ) {
    console.log(this.options); // {privateKey: 설정한값}
  }
  sign(userId: number): string {
    return jwt.sign({ id: userId }, this.options.privateKey);
  }
}
users.service.ts
jwtServiceが作成されているので、このように符号化できます.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateAccountInput } from './dtos/create-account.dto';
import { LoginInput } from './dtos/login.dto';
import { User } from './entities/user.entity';
import { ConfigService } from '@nestjs/config';
import { JwtService } from 'src/jwt/jwt.service';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly users: Repository<User>,
    private readonly config: ConfigService,
    private readonly jwtService: JwtService,
  ) {}

  //create User
  ...
  //Login User
  async login({
    email,
    password,
  }: LoginInput): Promise<{ ok: boolean; error?: string; token?: string }> {
    // make a JMT and give it to the user
    try {
      // find ths user with the email
      const user = await this.users.findOne({ email });
      if (!user) {
        return { ok: false, error: 'User not found' };
      }
      // check if the passowrd is correct
      const passwordCorrect = await user.checkPassword(password);
      if (!passwordCorrect) {
        return { ok: false, error: 'Wrong password' };
      }
      // const token = jwt.sign({ id: user.id }, this.config.get('PRIVATE_KEY'));
      const token = this.jwtService.sign(user.id);
      return { ok: true, token };
    } catch (error) {
      return { ok: false, error };
    }
  }
}