-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: Controller DTO 생성 * feat: Controller DTO 생성 * chore: NodeMailer 모듈 추가 * feat: RedisRepository CRUD 함수 추가 * feat: 인증코드 발급/검증 로직 * feat: 회원가입/로그인 구현 * fix: response가 void인 경우 content가 누락되던 현상 수정 * fix: MemberRepository 조회 시 password가 불러와지던 오류 수정 * feat: UserGuard 및 UserMiddleware 구현 * fix: 코드 인증시 redis 삭제 lazy * fix: 코드 인증시 가입 확인 * refactor: 리팩토링 * refactor: 리팩토링 * docs: Auth Swagger 문서화 * refactor: validate 오류 문자열 한글화 * refactor: import 최적화 * docs: Swagger 문서에서 인증 토큰 및 uid를 UUIDv4로 표기 * chore: 재사용을 위해 ValidationPipe Factory 생성 * fix: DTO Validation시 하나의 주요 규칙만 적용 * fix: 비밀번호 정규식 수정 (특수문자 선택적 포함) * test: Auth의 Request DTO 검증 테스트 추가 * chore: 테스트 폴더 분리 * test: Auth의 Request DTO 검증 테스트 * test: Auth Service 테스트 * test: Auth 통합 테스트 * docs: config 변화 README.md 업데이트 * test: test * test: rollback * chore: 누락된 yarn.lock 추가 * chore: 폴더 이동 (from dto/spec to spec) * chore: 폴더 이동 (from dto/spec to spec)
- Loading branch information
1 parent
6d97bef
commit b3fc882
Showing
39 changed files
with
2,241 additions
and
197 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,132 @@ | ||
import { Controller } from '@nestjs/common'; | ||
import { Body, Controller, Get, HttpCode, Post, Put, UseGuards } from '@nestjs/common'; | ||
import { ApiBearerAuth, ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger'; | ||
|
||
import { AuthService } from './auth.service'; | ||
import { AuthGuard } from './auth.guard'; | ||
import { ReqMember } from './auth.middleware'; | ||
|
||
import { Member } from '../member/member.schema'; | ||
|
||
import { LoginRequest } from './dto/request/LoginRequest'; | ||
import { LoginResponse } from './dto/response/LoginResponse'; | ||
import { RegisterRequest } from './dto/request/RegisterRequest'; | ||
import { SendCodeRequest } from './dto/request/SendCodeRequest'; | ||
import { VerifyCodeRequest } from './dto/request/VerifyCodeRequest'; | ||
import { VerifyCodeResponse } from './dto/response/VerifyCodeResponse'; | ||
import { MyInfoResponse } from './dto/response/MyInfoResponse'; | ||
|
||
import { ApiCustomResponse } from '../../utils/swagger/ApiCustomResponse.decorator'; | ||
import { ApiCustomErrorResponseDecorator } from '../../utils/swagger/ApiCustomErrorResponse.decorator'; | ||
|
||
import { MemberNotFoundException } from './exception/MemberNotFoundException'; | ||
import { WrongPasswordException } from './exception/WrongPasswordException'; | ||
import { InvalidVerifyTokenException } from './exception/InvalidVerifyTokenException'; | ||
import { AlreadyRegisteredByEmailException } from './exception/AlreadyRegisteredByEmailException'; | ||
import { AlreadyRegisteredByStudentIdException } from './exception/AlreadyRegisteredByStudentIdException'; | ||
import { InvalidVerifyCodeException } from './exception/InvalidVerifyCodeException'; | ||
|
||
@Controller('auth') | ||
export class AuthController {} | ||
@ApiTags('Auth') | ||
export class AuthController { | ||
constructor(private readonly service: AuthService) {} | ||
|
||
@Post() | ||
@HttpCode(200) | ||
@ApiOperation({ summary: '로그인' }) | ||
@ApiProperty({ type: LoginRequest }) | ||
@ApiCustomResponse({ type: LoginResponse, status: 200 }) | ||
@ApiCustomErrorResponseDecorator([ | ||
{ | ||
description: '회원을 찾을 수 없음', | ||
error: MemberNotFoundException, | ||
}, | ||
{ | ||
description: '비밀번호가 틀림', | ||
error: WrongPasswordException, | ||
}, | ||
]) | ||
async login(@Body() request: LoginRequest): Promise<LoginResponse> { | ||
const { email, password } = request; | ||
|
||
const token = await this.service.login(email, password); | ||
|
||
return { token }; | ||
} | ||
|
||
@Put() | ||
@HttpCode(201) | ||
@ApiOperation({ summary: '회원가입' }) | ||
@ApiProperty({ type: RegisterRequest }) | ||
@ApiCustomResponse({ status: 201 }) | ||
@ApiCustomErrorResponseDecorator([ | ||
{ | ||
description: '이메일 인증 토큰이 잘못됨', | ||
error: InvalidVerifyTokenException, | ||
}, | ||
{ | ||
description: '이미 가입된 이메일', | ||
error: AlreadyRegisteredByEmailException, | ||
}, | ||
{ | ||
description: '이미 가입된 학번', | ||
error: AlreadyRegisteredByStudentIdException, | ||
}, | ||
]) | ||
async register(@Body() request: RegisterRequest): Promise<void> { | ||
const { name, studentId, password, verifyToken } = request; | ||
|
||
await this.service.register(name, studentId, password, verifyToken); | ||
} | ||
|
||
@Get('/code') | ||
@HttpCode(201) | ||
@ApiOperation({ summary: '인증코드 전송' }) | ||
@ApiProperty({ type: SendCodeRequest }) | ||
@ApiCustomResponse({ status: 201 }) | ||
@ApiCustomErrorResponseDecorator([ | ||
{ | ||
description: '이미 가입된 이메일', | ||
error: AlreadyRegisteredByEmailException, | ||
}, | ||
]) | ||
async sendCode(@Body() request: SendCodeRequest): Promise<void> { | ||
const { email } = request; | ||
|
||
await this.service.sendCode(email); | ||
} | ||
|
||
@Post('/code') | ||
@HttpCode(200) | ||
@ApiOperation({ summary: '인증 토큰 발급' }) | ||
@ApiProperty({ type: VerifyCodeRequest }) | ||
@ApiCustomResponse({ type: VerifyCodeResponse, status: 200 }) | ||
@ApiCustomErrorResponseDecorator([ | ||
{ | ||
description: '잘못된 인증 코드', | ||
error: InvalidVerifyCodeException, | ||
}, | ||
]) | ||
async verifyCode(@Body() request: VerifyCodeRequest): Promise<VerifyCodeResponse> { | ||
const { email, code } = request; | ||
|
||
const verifyToken = await this.service.verifyCode(email, code); | ||
|
||
return { verifyToken }; | ||
} | ||
|
||
@Get('/me') | ||
@HttpCode(200) | ||
@UseGuards(AuthGuard) | ||
@ApiOperation({ summary: '인증 토큰으로 정보 조회' }) | ||
@ApiBearerAuth() | ||
@ApiCustomResponse({ type: MyInfoResponse, status: 200 }) | ||
async getMyInfo(@ReqMember() member: Member): Promise<MyInfoResponse> { | ||
const memberDoc = member['_doc']; | ||
const memberId = member['_id']; | ||
|
||
delete memberDoc['_id']; | ||
delete memberDoc['__v']; | ||
|
||
return { userId: memberId, ...memberDoc }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; | ||
import { ConfigService } from '@nestjs/config'; | ||
|
||
import * as jwt from 'jsonwebtoken'; | ||
|
||
import { MemberRepository } from '../member/member.repository'; | ||
|
||
@Injectable() | ||
export class AuthGuard implements CanActivate { | ||
private readonly jwtSecret: string; | ||
|
||
constructor( | ||
private configService: ConfigService, | ||
private repository: MemberRepository, | ||
) { | ||
this.jwtSecret = this.configService.get<string>('jwt.secret'); | ||
} | ||
|
||
async canActivate(context: ExecutionContext): Promise<boolean> { | ||
const request = context.switchToHttp().getRequest(); | ||
const authorization = request.headers['authorization']; | ||
|
||
if (authorization && authorization.startsWith('Bearer ')) { | ||
const token = authorization.slice(7); | ||
|
||
if (jwt.verify(token, this.jwtSecret)) { | ||
const id = jwt.decode(authorization.slice(7))['id']; | ||
|
||
return await this.repository.existsById(id); | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { createParamDecorator, ExecutionContext, Injectable, NestMiddleware } from '@nestjs/common'; | ||
import { NextFunction } from 'express'; | ||
|
||
import { MemberRepository } from '../member/member.repository'; | ||
|
||
import * as jwt from 'jsonwebtoken'; | ||
|
||
@Injectable() | ||
export class AuthMiddleware implements NestMiddleware { | ||
constructor(private repository: MemberRepository) {} | ||
|
||
async use(req: Request, res: Response, next: NextFunction) { | ||
const authorization = req.headers['authorization']; | ||
|
||
if (authorization) { | ||
const id = jwt.decode(authorization.slice(7))['id']; | ||
|
||
req['member'] = await this.repository.findById(id); | ||
} | ||
|
||
next(); | ||
} | ||
} | ||
|
||
export const ReqMember = createParamDecorator((data: any, ctx: ExecutionContext) => { | ||
const request = ctx.switchToHttp().getRequest(); | ||
|
||
return request.member; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.