11) 개인 프로젝트) NestJS Auth 환경변수 설정 / Sign Out 기능

Leo·2021년 2월 23일
3

Project-01

목록 보기
11/12
post-thumbnail

Auth 환경 변수 설정

기존에 DB정보를 설정했던 .env 환경변수 설정 파일에 JWT의 SECRET 정보를 추가합니다.

.env 수정

Secret 정보와 JWT 정보의 만료 시간을 작성합니다.

...생략

JWT_SECRET=TestSecretKey
JWT_EXPIRATION_TIME=30

만료시간은 확인을 위해 30초로 설정합니다.

이전에 생성한 ormconfig.json파일과 constants.ts 파일은 삭제합니다.

검증 추가

이전에 Joi를 이용하여 환경변수를 검증했습니다. 똑같이 검증 절차를 걸치도록 AppModule에 추가합니다.

/src/app.module.ts

...생략

@Module({
  imports: [
    ConfigModule.forRoot({
      validationSchema: Joi.object({
		...생략
        JWT_SECRET: Joi.string().required(),
        JWT_EXPIRATION_TIME: Joi.string().required(),
      }),
    }),
    UsersModule,
    AuthModule,
    DatabaseModule,
  ],
  controllers: [AppController],
  providers: [AppService, { provide: APP_GUARD, useClass: JwtAuthGuard }],
})
export class AppModule {}

Auth Module에 추가하기

auth module에는 환경 변수에서 가져와 검증까지 걸쳐 들어온 JWT_SECRET 값과 JWT_EXPIRATION_TIME 값을 넣어줘야합니다.

/src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { JwtStrategy } from './strategies/jwt.strategy';
import { LocalStrategy } from './strategies/local.strategy';
import { AuthController } from './auth.controller';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    ConfigModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        secret: configService.get('JWT_SECRET'),
        signOptions: {
          expiresIn: `${configService.get('JWT_EXPIRATION_TIME')}s`,
        },
      }),
    }),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService, JwtModule],
  controllers: [AuthController],
})
export class AuthModule {}

JWT Strategy 수정

사용자가 데이터를 요청할 때에는 쿠키에 존재하는 JWT의 Secret키와 비교를 위해 Config에서 Secret 값을 가져옵니다.

/src/auth/strategies/jwt.strategy.ts

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { UsersService } from '../../users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    private readonly configService: ConfigService,
    private readonly usersService: UsersService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromExtractors([
        (request) => {
          return request?.cookies?.Authentication;
        },
      ]),
      secretOrKey: configService.get('JWT_SECRET'),
    });
  }

  async validate(payload: any) {
    return this.usersService.getById(payload.id);
  }
}

기타 수정 사항

/src/users/usersService.ts

...생략

@Injectable()
export class UsersService {
  ...생략
  async getById(id: number) {
    const user = await this.usersRepository.findOne({ id });
    if (user) {
      return user;
    }

    throw new HttpException(
      'User with this id does not exist',
      HttpStatus.NOT_FOUND,
    );
  }
}

/src/auth/auth.service.ts

...생략

@Injectable()
export class AuthService {
  ...생략
  async login(user: User) {
    const payload = { email: user.email, id: user.id };
    const token = this.jwtService.sign(payload);
    return token;
  }
  ...생략
}

Auth 로그아웃 기능

로그인 기능이 있으니 같은 방식으로 로그아웃 기능을 만들어 보려고 합니다. 로그아웃은 쿠키에 있는 Token값을 초기화 해주면 됩니다.

Auth Service에 Login 수정과 Logout 추가

기존 로그인 기능의 쿠키 설정 부분이 Controller에 있었기 때문에 그부분은 Service로 이동시키도록하고 logout 메소드도 추가해보겠습니다.

/src/auth/auth.service.ts

...생략

@Injectable()
export class AuthService {
  constructor(
    private readonly usersService: UsersService,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
  ) {}

  ...생략
  async login(user: User) {
    const payload = { email: user.email, id: user.id };
    const token = this.jwtService.sign(payload);
    return {
      token: token,
      domain: 'localhost',
      path: '/',
      httpOnly: true,
      maxAge: Number(this.configService.get('JWT_EXPIRATION_TIME')) * 1000,
    };
  }

  async logOut() {
    return {
      token: '',
      domain: 'localhost',
      path: '/',
      httpOnly: true,
      maxAge: 0,
    };
  }
  ...생략
}

auth controller logout 경로 추가

기존 방식에서 약간의 변화를 주었습니다. 토큰만 반환 되던 방식이 아닌 토큰과 옵션 두가지가 포함됩니다. 반환된 결과는 token 부분과 Option 부분을 나누어 저장하여 사용합니다.

/src/auth/auth.controller.ts

...생략

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

  @Public()
  @UseGuards(LocalAuthGuard)
  @Post('login')
  async login(@Req() req, @Res({ passthrough: true }) res: Response) {
    const { token, ...option } = await this.authService.login(req.user);
    res.cookie('Authentication', token, option);
  }

  @Post('logout')
  async logOut(@Res({ passthrough: true }) res: Response) {
    const { token, ...option } = await this.authService.logOut();
    res.cookie('Authentication', token, option);
  }

  ...생략
}

성공적으로 완료 했다면 login 후 logout하면 cookie의 Token값이 빈공간으로 처리된다.

profile
개발자

0개의 댓글