JWT module

김종민·2022년 6월 27일
0

Nuber-Server

목록 보기
9/34


들어가기
JWT를 이용해서 token을 발급하는 것을
module로 만들어서 사용해 본다.
또한, middleware로 만들어서 사용해본다.
module은 두가지로 구분된다. UserModule같은 static모듈,
ConfingModule같은 Dynamic모듈로

https://docs.nestjs.com/modules#dynamic-modules

jwt.io

  1. $nest g mo jwt

1. jwt/jwt.constant.ts

export const CONFIG_OPTIONS = 'CONFIG_OPTIONS';

CONFIG_OPTIONS라고 단어를 하나 박아놓는다.

2. jwt/jwt.interfaces.ts

export interface JwtModuleOptions {
  privateKey: string;
}

JwtModuleOptions라는 interface를 만들어 privateKey를 string으로 type화 한다.

3. jwt/jwt.module.ts

import { DynamicModule, Global, Module } from '@nestjs/common';
import { CONFIG_OPTIONS } from './jwt.constants';
import { JwtModuleOptions } from './jwt.interfaces';
import { JwtService } from './jwt.service';

@Module({})
@Global() ///Global로 지정하면 다른 module에서 사용할 떄, imports를 하지 않아도 된다
         ///그래서 이왕이면, Global()로 만든다.
export class JwtModule {
  static forRoot(options: JwtModuleOptions): DynamicModule {
  ///Dynamic모듈을 만들기 위하 방법, options의 type은 
  ///jwt.interfaces.ts의 JwtModuleOptions를 type으로 한다.
  ///options부분은 .env.dev의 PRIVATE_KEY부분인데, 이거 사용
  ///setting는 app.module.ts에서 한다.
  
    return {
      module: JwtModule,
      providers: [
        {
          provide: CONFIG_OPTIONS,
          useValue: options,  =>JwtModuleOptions안의 PRIVATE_KEY 값임,
        },
        JwtService,
      ],
      ///CONFIG_OPTIONS라는 key로 options(PRIVATE_KEY)를
      ///JwtService에서 사용할 수 있게끔 한다.
      ///JwtService를 보면, 이해가 더 잘됨.
      
      exports: [JwtService],
      ///JwtService를 다른 module에서 사용할 수 있게, exports 해 놓는다.
    };
  }
}


4. jwt/jwt.service.ts

import { Inject, Injectable } from '@nestjs/common';
import { CONFIG_OPTIONS } from './jwt.constants';
import { JwtModuleOptions } from './jwt.interfaces';
import * as jwt from 'jsonwebtoken'; ///jsonwebtoken사용을 위한 import

@Injectable() ///service.ts는 거의 Injectable()  type이다.
export class JwtService {
  constructor(
    @Inject(CONFIG_OPTIONS) private readonly options: JwtModuleOptions,
  ) {}
  ///constructor에 CONFIG_OPTIONS이라는 key를 넣는다.
  ///options: JwtModuleOptions 를 적어주므로써, PRIVATE_KEY에 접근할 수 있게한다.
  
  sign(payload: object): string {
    return jwt.sign(payload, this.options.privateKey);
  }
  ///token을 만들어 주는 함수, 다른 모듈에서 JwtService.sign으로 사용가능
  
  verify(token: string) {
    return jwt.verify(token, this.options.privateKey);
  }
   ///token을 확인해 주는 함수, 다른 모듈에서 JwtService.verify로 사용가능
}

5. app.module.ts

.env.dev의 PRIVATE_KEY가 어떻게 jwt모듈로 넘어가는지를 잘 확인한다.

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: process.env.NODE_ENV === 'dev' ? '.env.dev' : '.env.test',
      ignoreEnvFile: process.env.NODE_ENV === 'prod',
      validationSchema: Joi.object({
        NODE_ENV: Joi.string().valid('dev', 'prod').required(),
        DB_HOST: Joi.string().required(),
        DB_PORT: Joi.string().required(),
        DB_USERNAME: Joi.string().required(),
        DB_PASSWORD: Joi.string().required(),
        DB_NAME: Joi.string().required(),
        PRIVATE_KEY: Joi.string().required(),
      }),
    }),
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: process.env.DB_HOST,
      port: +process.env.DB_PORT,
      username: process.env.DB_USERNAME,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      synchronize: true,
      logging: true,
      entities: [User],
    }),
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: true,
      context: ({ req }) => ({ user: req['user'] }),
    }),
    ///////////////////////////////
    JwtModule.forRoot({
      privateKey: process.env.PRIVATE_KEY,
    }),
    ///위와같이 지정해 주면, interface.ts의 JwtModuleOption에 들어감.
    //Jwt모듈 안에서 PRIVATE_KEY를 사용가능하게 됨.
    ////////////////////////////
    RestaurantModule,
    CommonModule,
    UsersModule,
    AuthModule,
  ],

6. 사용 (users.service.ts)

  
@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User) private readonly users: Repository<User>,
    private readonly config: ConfigService,
    private readonly jwtService: JwtService,
  ) {}
///private readonly jwtSetvice: JwtSservice를 적어줌으로써,
///jwt.service.ts의 sign과 verify를 사용할 수 있게 된다.
///login으로 넘어갈것

  async createAccount({
    email,
    password,
    role,
  }: CreateAccountInput): Promise<{ ok: boolean; error?: string }> {
    try {
      const exists = await this.users.findOneBy({ email });
      if (exists) {
        return { ok: false, error: 'There is a user with that email already' };
      }
      await this.users.save(this.users.create({ email, password, role }));
      return { ok: true };
    } catch (e) {
      return { ok: false, error: 'Could not create account' };
    }
  }

  async login({
    email,
    password,
  }: LoginInput): Promise<{ ok: boolean; error?: string; token?: string }> {
    try {
      const user = await this.users.findOneBy({ email });
      if (!user) {
        return {
          ok: false,
          error: 'User not found',
        };
      }
      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({ id: user.id });
      ///jwt.service.ts에서 만든 sign을 사용함.{id:user.id}를 넘겨서 token만듬.
      
      return {
        ok: true,
        token,
      };
      } catch (error) {
      return {
        ok: false,
        error,
      };
    }
  1. 모듈을 dynamic으로 만들면, 다른 module에서 가져다 사용할 수 있다.
    1-1. 모듈을 dynamic으로 만드는 방법을 확인한다.
  2. jwt모듈에서 .env.dev의 PRIVATE_KEY를 가져다 쓰는 법을 잘 알아놓을것.
  3. jwt모듈의 serveice에서 만들어 놓은, 함수들을 가져다 사용하는 방법을 잘 알아놓자.
profile
코딩하는초딩쌤

0개의 댓글