diff --git a/BE/package-lock.json b/BE/package-lock.json index a3d4e28..e715395 100644 --- a/BE/package-lock.json +++ b/BE/package-lock.json @@ -7489,7 +7489,7 @@ "pkginfo": "~0.3.0" } }, - "node_modules/passport-kakao/node_modules/passport-oauth2": { + "node_modules/passport-oauth2": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==", @@ -7540,25 +7540,6 @@ "url": "https://github.com/sponsors/jaredhanson" } }, - "node_modules/passport-oauth2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", - "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, "node_modules/passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", diff --git a/BE/src/auth/auth.controller.ts b/BE/src/auth/auth.controller.ts index 25a3669..a32084a 100644 --- a/BE/src/auth/auth.controller.ts +++ b/BE/src/auth/auth.controller.ts @@ -24,6 +24,16 @@ import { AuthGuard } from "@nestjs/passport"; export class AuthController { constructor(private authService: AuthService) {} + @Get("/kakao/callback") + @UseGuards(AuthGuard("kakao")) + @HttpCode(201) + async kakaoSignIn( + @GetUser() user: User, + @Req() request: Request, + ): Promise { + return await this.authService.kakaoSignIn(user, request); + } + @Get("/naver/callback") @UseGuards(AuthGuard("naver")) @HttpCode(201) diff --git a/BE/src/auth/auth.module.ts b/BE/src/auth/auth.module.ts index 6b509a7..9291a81 100644 --- a/BE/src/auth/auth.module.ts +++ b/BE/src/auth/auth.module.ts @@ -10,6 +10,7 @@ import { TypeOrmModule } from "@nestjs/typeorm"; import { User } from "./users.entity"; import { UsersRepository } from "./users.repository"; import { DiariesRepository } from "src/diaries/diaries.repository"; +import { KakaoStrategy } from "./strategies/kakao.strategy"; import { NaverOAuthStrategy } from "./strategies/naver.strategy"; @Module({ @@ -27,6 +28,7 @@ import { NaverOAuthStrategy } from "./strategies/naver.strategy"; providers: [ AuthService, JwtStrategy, + KakaoStrategy, UsersRepository, PrivateDiaryGuard, DiariesRepository, diff --git a/BE/src/auth/auth.service.ts b/BE/src/auth/auth.service.ts index c13c127..8691cfa 100644 --- a/BE/src/auth/auth.service.ts +++ b/BE/src/auth/auth.service.ts @@ -33,27 +33,11 @@ export class AuthService { throw new NotFoundException("존재하지 않는 아이디입니다."); } - if (await bcrypt.compare(password, user.password)) { - const accessTokenPayload = { userId }; - const accessToken = await this.jwtService.sign(accessTokenPayload, { - expiresIn: "1h", - }); - - const refreshTokenPayload = { - requestIp: request.ip, - accessToken: accessToken, - }; - const refreshToken = await this.jwtService.sign(refreshTokenPayload, { - expiresIn: "24h", - }); - - // 86000s = 24h - await this.redisClient.set(userId, refreshToken, "EX", 86400); - - return new AccessTokenDto(accessToken); - } else { + if (!(await bcrypt.compare(password, user.password))) { throw new NotFoundException("올바르지 않은 비밀번호입니다."); } + + return this.createUserTokens(userId, request.ip); } async signOut(user: User): Promise { @@ -69,43 +53,43 @@ export class AuthService { const expiredResult = JSON.parse(payload.toString()); const userId = expiredResult.userId; - const accessTokenPayload = { userId }; - const accessToken = await this.jwtService.sign(accessTokenPayload, { - expiresIn: "1h", - }); + return this.createUserTokens(userId, request.ip); + } - const refreshTokenPayload = { - requestIp: request.ip, - accessToken: accessToken, - }; - const refreshToken = await this.jwtService.sign(refreshTokenPayload, { - expiresIn: "24h", - }); + async naverSignIn(user: User, request: Request): Promise { + const userId = user.userId; + const provider = providerEnum.NAVER; - // 86000s = 24h - await this.redisClient.set(userId, refreshToken, "EX", 86400); + if (!(await User.findOne({ where: { userId, provider } }))) { + await user.save(); + } - return new AccessTokenDto(accessToken); + return this.createUserTokens(userId, request.ip); } - async naverSignIn(user: User, request: Request): Promise { - if ( - !(await User.findOne({ - where: { userId: user.userId, provider: providerEnum.NAVER }, - })) - ) { + async kakaoSignIn(user: User, request: Request): Promise { + const userId = user.userId; + const provider = providerEnum.KAKAO; + + if (!(await User.findOne({ where: { userId, provider } }))) { await user.save(); } - const userId = user.userId; + return this.createUserTokens(userId, request.ip); + } + + private async createUserTokens( + userId: string, + requestIp: string, + ): Promise { const accessTokenPayload = { userId }; const accessToken = await this.jwtService.sign(accessTokenPayload, { expiresIn: "1h", }); const refreshTokenPayload = { - requestIp: request.ip, - accessToken: accessToken, + requestIp, + accessToken, }; const refreshToken = await this.jwtService.sign(refreshTokenPayload, { expiresIn: "24h", diff --git a/BE/src/auth/strategies/kakao.strategy.ts b/BE/src/auth/strategies/kakao.strategy.ts new file mode 100644 index 0000000..6252024 --- /dev/null +++ b/BE/src/auth/strategies/kakao.strategy.ts @@ -0,0 +1,49 @@ +import "dotenv/config"; +import { BadRequestException, Injectable } from "@nestjs/common"; +import { PassportStrategy } from "@nestjs/passport"; +import { Profile, Strategy } from "passport-kakao"; +import { providerEnum } from "src/utils/enum"; +import { User } from "../users.entity"; +import * as bcrypt from "bcryptjs"; + +@Injectable() +export class KakaoStrategy extends PassportStrategy(Strategy) { + constructor() { + super({ + clientID: process.env.KAKAO_CLIENT_ID, + clientSecret: process.env.KAKAO_CLIENT_SECRET, + callbackURL: `${process.env.FRONTEND_URL}/auth/kakao/callback`, + }); + } + + async validate( + accessToken: string, + refreshToken: string, + profile: Profile, + done: (error: any, user?: any, info?: any) => void, + ) { + try { + const { _json } = profile; + const { id, kakao_account, properties } = _json; + + const user = new User(); + + const salt = await bcrypt.genSalt(); + const hashedPassword = await bcrypt.hash(id.toString(), salt); + + user.email = kakao_account.email; + user.userId = id.toString() + "*kakao"; + user.nickname = properties.nickname; + user.password = hashedPassword; + user.provider = providerEnum.KAKAO; + + done(null, user); + } catch (error) { + done( + new BadRequestException( + `카카오 로그인 중 오류가 발생했습니다 : ${error.message}`, + ), + ); + } + } +} diff --git a/BE/src/auth/strategies/naver.strategy.ts b/BE/src/auth/strategies/naver.strategy.ts index d9d83d9..f7a20ff 100644 --- a/BE/src/auth/strategies/naver.strategy.ts +++ b/BE/src/auth/strategies/naver.strategy.ts @@ -1,8 +1,10 @@ +import "dotenv/config"; import { Injectable, BadRequestException } from "@nestjs/common"; import { PassportStrategy } from "@nestjs/passport"; import { Profile, Strategy } from "passport-naver"; import { User } from "../users.entity"; import { providerEnum } from "src/utils/enum"; +import * as bcrypt from "bcryptjs"; @Injectable() export class NaverOAuthStrategy extends PassportStrategy(Strategy, "naver") { @@ -23,10 +25,13 @@ export class NaverOAuthStrategy extends PassportStrategy(Strategy, "naver") { const { email, id, nickname } = profile; const user = new User(); + const salt = await bcrypt.genSalt(); + const hashedPassword = await bcrypt.hash(id, salt); + user.email = email; user.userId = id + "*naver"; user.nickname = nickname; - user.password = "naver"; + user.password = hashedPassword; user.provider = providerEnum.NAVER; return user; diff --git a/BE/src/auth/users.repository.ts b/BE/src/auth/users.repository.ts index e77c642..2482edb 100644 --- a/BE/src/auth/users.repository.ts +++ b/BE/src/auth/users.repository.ts @@ -1,7 +1,6 @@ import { ConflictException, InternalServerErrorException, - NotFoundException, } from "@nestjs/common"; import { CreateUserDto } from "./dto/users.dto"; import { User } from "./users.entity"; @@ -34,11 +33,7 @@ export class UsersRepository { try { await user.save(); } catch (error) { - if (error.code === "ER_DUP_ENTRY") { - throw new ConflictException("Existing userId"); - } else { - throw new InternalServerErrorException(); - } + throw new InternalServerErrorException(error); } return user;