Skip to content

Commit

Permalink
Merge pull request #212 from boostcampwm2023/feat/191-kakao-login
Browse files Browse the repository at this point in the history
[Feat] 카카오 OAuth 로그인 API 구현
  • Loading branch information
mingxoxo authored Dec 5, 2023
2 parents 52a964d + b906398 commit 1cb80a3
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 69 deletions.
21 changes: 1 addition & 20 deletions BE/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AccessTokenDto> {
return await this.authService.kakaoSignIn(user, request);
}

@Get("/naver/callback")
@UseGuards(AuthGuard("naver"))
@HttpCode(201)
Expand Down
2 changes: 2 additions & 0 deletions BE/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -27,6 +28,7 @@ import { NaverOAuthStrategy } from "./strategies/naver.strategy";
providers: [
AuthService,
JwtStrategy,
KakaoStrategy,
UsersRepository,
PrivateDiaryGuard,
DiariesRepository,
Expand Down
68 changes: 26 additions & 42 deletions BE/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
Expand All @@ -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<AccessTokenDto> {
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<AccessTokenDto> {
if (
!(await User.findOne({
where: { userId: user.userId, provider: providerEnum.NAVER },
}))
) {
async kakaoSignIn(user: User, request: Request): Promise<AccessTokenDto> {
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<AccessTokenDto> {
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",
Expand Down
49 changes: 49 additions & 0 deletions BE/src/auth/strategies/kakao.strategy.ts
Original file line number Diff line number Diff line change
@@ -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}`,
),
);
}
}
}
7 changes: 6 additions & 1 deletion BE/src/auth/strategies/naver.strategy.ts
Original file line number Diff line number Diff line change
@@ -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") {
Expand All @@ -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;
Expand Down
7 changes: 1 addition & 6 deletions BE/src/auth/users.repository.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
ConflictException,
InternalServerErrorException,
NotFoundException,
} from "@nestjs/common";
import { CreateUserDto } from "./dto/users.dto";
import { User } from "./users.entity";
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 1cb80a3

Please sign in to comment.