diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 2cee2d0..5fcc894 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -27,8 +27,8 @@ export class AuthController { const { userId, naverAccessToken, naverRefreshToken } = await this.authService.naverLogin(code); - const accessToken = await this.authService.createAccessToken(userId); - const refreshToken = await this.authService.createRefreshToken(userId); + const accessToken = await this.tokenService.createAccessToken(userId); + const refreshToken = await this.tokenService.createRefreshToken(userId); await this.tokenService.saveTokens(userId, refreshToken, naverAccessToken, naverRefreshToken); @@ -47,23 +47,26 @@ export class AuthController { } const { userId, kakaoAccessToken, kakaoRefreshToken } = await this.authService.kakaoLogin(code); - const accessToken = await this.authService.createAccessToken(userId); - const refreshToken = await this.authService.createRefreshToken(userId); + const accessToken = await this.tokenService.createAccessToken(userId); + const refreshToken = await this.tokenService.createRefreshToken(userId); await this.tokenService.saveTokens(userId, refreshToken, kakaoAccessToken, kakaoRefreshToken); return res.json({ accessToken, refreshToken }); } + @ApiOperation({ summary: '액세스 토큰 재발급 API', description: '액세스 토큰 재발급 API' }) + // @ApiResponse() @Get('new-access-token') - async newAccessToken(@Headers('refresh_token') refreshToken: string) { - const userId = await this.authService.decodeToken(refreshToken); - return await this.authService.createAccessToken(userId); + async newAccessToken(@Headers('refresh_token') refreshToken: string, @Res() res) { + const userId = await this.tokenService.decodeToken(refreshToken); + const newAccessToken = await this.tokenService.createAccessToken(userId); + return res.json({ accessToken: newAccessToken }); } @Post('kakao/logout') async kakaoLogout(@Headers('access_token') accessToken: string) { - const userId = await this.authService.decodeToken(accessToken); + const userId = await this.tokenService.decodeToken(accessToken); const tokens = await this.tokenService.getUserTokens(userId); let kakaoAccessToken = tokens[0].socialAccessToken; @@ -80,7 +83,7 @@ export class AuthController { @Post('kakao/unlink') async kakaoUnlink(@Headers('access_token') accessToken: string) { - const userId = await this.authService.decodeToken(accessToken); + const userId = await this.tokenService.decodeToken(accessToken); const tokens = await this.tokenService.getUserTokens(userId); let kakaoAccessToken = tokens[0].socialAccessToken; @@ -97,14 +100,14 @@ export class AuthController { @Post('naver/logout') async naverLogout(@Headers('access_token') accessToken: string) { - const userId = await this.authService.decodeToken(accessToken); + const userId = await this.tokenService.decodeToken(accessToken); return await this.tokenService.deleteTokens(userId); } @Post('naver/unlink') async naverUnlink(@Headers('access_token') accessToken: string) { - const userId = await this.authService.decodeToken(accessToken); + const userId = await this.tokenService.decodeToken(accessToken); const tokens = await this.tokenService.getUserTokens(userId); let naverAccessToken = tokens[0].socialAccessToken; @@ -121,7 +124,7 @@ export class AuthController { @Delete('account') async accountDelete(@Headers('access_token') accessToken: string) { - const userId = await this.authService.decodeToken(accessToken); + const userId = await this.tokenService.decodeToken(accessToken); await this.s3Service.deleteImagesWithPrefix(userId + '_'); return await this.authService.accountDelete(userId); } diff --git a/src/auth/services/auth.service.ts b/src/auth/services/auth.service.ts index bf5162d..7ed1a54 100644 --- a/src/auth/services/auth.service.ts +++ b/src/auth/services/auth.service.ts @@ -1,6 +1,5 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { UserRepository } from 'src/users/repositories/user.repository'; -import * as jwt from 'jsonwebtoken'; import * as dotenv from 'dotenv'; import { UserImageRepository } from 'src/users/repositories/user-image.repository'; import axios from 'axios'; @@ -209,53 +208,4 @@ export class AuthService { } return "사용자 계정 삭제에 성공했습니다."; } - - async createAccessToken(userId: number) { - const jwtSecretKey = process.env.JWT_SECRET_KEY; - const payload = { - sub: "accessToken", - userId, - exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1시간 - }; - - const accessToken = jwt.sign(payload, jwtSecretKey); - - return accessToken; - } - - async createRefreshToken(userId: number) { - const jwtSecretKey = process.env.JWT_SECRET_KEY; - const payload = { - sub: "refreshToken", - userId, - exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 7), // 7일 - }; - const refreshToken = jwt.sign(payload, jwtSecretKey); - - return refreshToken; - } - - async newAccessToken(refreshToken: string) { - const jwtSecretKey = process.env.JWT_SECRET_KEY; - const payload = jwt.verify(refreshToken, jwtSecretKey); - - const userId = payload['userId']; - const newAccessToken = await this.createAccessToken(userId); - return newAccessToken; - } - - async verifyToken(accessToken: string) { - const jwtSecretKey = process.env.JWT_SECRET_KEY; - const verifyToken = jwt.verify(accessToken, jwtSecretKey); - if (verifyToken) { - return { status: true, message: "유효한 토큰입니다." }; - } - return { status: false, message: "유효하지 않은 토큰입니다." }; - } - - async decodeToken(accessToken: string) { - const payload = jwt.decode(accessToken); - const userId = payload['userId']; - return userId; - } } diff --git a/src/auth/services/token.service.ts b/src/auth/services/token.service.ts index f469f8d..45bd998 100644 --- a/src/auth/services/token.service.ts +++ b/src/auth/services/token.service.ts @@ -1,5 +1,6 @@ -import { Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { TokenRepository } from '../repositories/token.repository'; +import * as jwt from 'jsonwebtoken'; import axios from 'axios'; @Injectable() @@ -99,4 +100,70 @@ export class TokenService { return { status: false, message: '토큰 삭제 실패' }; } } + + async verifyToken(token: string) { + try { + const jwtSecretKey = process.env.JWT_SECRET_KEY; + const userId = jwt.verify(token, jwtSecretKey)['userId']; + const tokenType = jwt.verify(token, jwtSecretKey)['sub']; + if (tokenType === 'refreshToken') { + const dbRefreshToken = await this.tokenRepository.getUserTokens(userId)[0].refreshToken; + if (token !== dbRefreshToken) { + throw new HttpException('유효하지 않은 토큰입니다.', HttpStatus.FORBIDDEN); + } + } + return { status: true, message: "유효한 토큰입니다." }; + } catch (error) { + if (error.message == 'jwt expired') { + throw new HttpException('만료된 토큰입니다.', HttpStatus.FORBIDDEN); + } else if (error.message == 'invalid token' || error.message == 'invalid signature') { + throw new HttpException('유효하지 않은 토큰입니다.', HttpStatus.FORBIDDEN); + } else if (error.message == 'jwt must be provided') { + throw new HttpException('토큰이 제공되지 않았습니다.', HttpStatus.FORBIDDEN); + } else { + throw new HttpException('토큰 검증에 실패했습니다.', HttpStatus.FORBIDDEN); + } + } + } + + async decodeToken(token: string) { + await this.verifyToken(token); + const payload = jwt.decode(token); + const userId = payload['userId']; + return userId; + } + + async createAccessToken(userId: number) { + const jwtSecretKey = process.env.JWT_SECRET_KEY; + const payload = { + sub: "accessToken", + userId, + exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1시간 + }; + + const accessToken = jwt.sign(payload, jwtSecretKey); + + return accessToken; + } + + async createRefreshToken(userId: number) { + const jwtSecretKey = process.env.JWT_SECRET_KEY; + const payload = { + sub: "refreshToken", + userId, + exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 7), // 7일 + }; + const refreshToken = jwt.sign(payload, jwtSecretKey); + + return refreshToken; + } + + async newAccessToken(refreshToken: string) { + const jwtSecretKey = process.env.JWT_SECRET_KEY; + const payload = jwt.verify(refreshToken, jwtSecretKey); + + const userId = payload['userId']; + const newAccessToken = await this.createAccessToken(userId); + return newAccessToken; + } }