Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions src/config/jwt.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ export const jwtStrategy = new JwtStrategy(
},
});

//μˆ˜μ • μ „
if (!user) return done(null, false);
// if (!user || user.deletedAt) {
// return done(null, false);
// }
if (!user || user.deletedAt) {
return done(null, false);
}

return done(null, user);
} catch (err) {
Expand Down
58 changes: 31 additions & 27 deletions src/controllers/auth.controller.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { KakaoAuthService } from "../services/auth.service.js";
import { UnauthorizedError } from "../errors/custom.error.js";
import { BadRequestError, UnauthorizedError } from "../errors/custom.error.js";
// import { prisma } from "../db.config.js";

export class AuthController{
Expand Down Expand Up @@ -76,38 +76,42 @@ export class AuthController{
}
}

// //μž¬κ°€μž…μ‹œ κΈ°μ‘΄ 정보 볡ꡬ
// async restore(req, res, next){
// try{
// const { providerId } = req.body;

// if (!providerId) {throw new BadRequestError("PROVIDER_ID_REQUIRED","providerIdκ°€ ν•„μš”ν•©λ‹ˆλ‹€.");}
//μž¬κ°€μž…μ‹œ κΈ°μ‘΄ 정보 볡ꡬ
async restore(req, res, next) {
try {
const { token } = req.body;

// const user = await prisma.user.findFirst({
// where:{
// provider: "KAKAO",
// providerId,
// deletedAt: { not: null },
// }
// });
if (!token) {
throw new BadRequestError("TOKEN_REQUIRED", "볡ꡬ 토큰이 ν•„μš”ν•©λ‹ˆλ‹€.");
}

// if(!user){ throw new BadRequestError("USER_NOT_FOUND","볡ꡬ할 νƒˆν‡΄ 계정을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.");}
const { accessToken, refreshToken } =
await this.kakaoAuthService.restoreKakaoUser(token);

// await prisma.user.update({
// where:{ id: user.id },
// data:{ deletedAt: null }
// });
const isProd = process.env.NODE_ENV === "production";

// return res.status(200).json({
// resultType:"SUCCESS",
// message:"계정이 λ³΅κ΅¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€."
// });
// refreshToken μΏ ν‚€ μ„ΈνŒ…
res.cookie("refreshToken", refreshToken, {
httpOnly: true,
secure: isProd,
sameSite: isProd ? "none" : "lax",
path: "/",
maxAge: 1000 * 60 * 60 * 24 * 14,
});

// }catch(error){
// next(error);
// }
// }
return res.status(200).json({
resultType: "SUCCESS",
message: "계정이 λ³΅κ΅¬λ˜μ—ˆμœΌλ©° λ‘œκ·ΈμΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.",
data: {
accessToken,
accessTokenExpireIn: 3600,
},
});

} catch (error) {
next(error);
}
}
}


Expand Down
21 changes: 11 additions & 10 deletions src/routes/auth.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ router.get(
const redirectBaseUrl = REDIRECT_URL_MAP[req.query.state || "prod"];
if (!redirectBaseUrl) { return res.status(500).send("λ¦¬λ‹€μ΄λ ‰νŠΈ URL이 μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."); }

// //νƒˆν‡΄ νšŒμ› λΆ„κΈ°
// if (req.user.withdrawnUser) {
// return res.redirect(`${redirectBaseUrl}/auth/withdrawn`);
// }
// νƒˆν‡΄ νšŒμ› λΆ„κΈ°
if (req.user.withdrawnUser) {
return res.redirect(`${redirectBaseUrl}/login?status=withdrawn&token=${req.user.restoreToken}`);
}


const { refreshToken } = req.user;
const isProd = process.env.NODE_ENV === "production";
Expand Down Expand Up @@ -102,11 +103,11 @@ router.delete(
router.post("/logout", authController.logout.bind(authController));
//Access Token λ°œκΈ‰
router.post("/refresh", authController.refresh.bind(authController));
// //μž¬κ°€μž…μ‹œ 볡ꡬ
// router.post(
// "/restore",
// // passport.authenticate("jwt", { session: false }),
// authController.restore.bind(authController)
// );
//μž¬κ°€μž…μ‹œ 볡ꡬ
router.post(
"/restore",
authController.restore.bind(authController)
);


export default router;
81 changes: 62 additions & 19 deletions src/services/auth.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,26 +98,23 @@ export class KakaoAuthService {
});

let isNewUser = false;

// νƒˆν‡΄ μ‚¬μš©μž 차단
if (user && user.deletedAt) {
const restoreToken = crypto.randomUUID();

//νƒˆν‡΄ μ‚¬μš©μžλ©΄ μžλ™ 볡ꡬ
if(user && user.deletedAt){
user = await prisma.user.update({
where: {
id: user.id
},
data: {
deletedAt: null,
},
});
await redis.set(
`restore_token:${restoreToken}`,
providerId,
"EX",
60 * 5 //5λΆ„
);

return {
withdrawnUser: true,
restoreToken,
};
}

// // νƒˆν‡΄ μ‚¬μš©μž 차단
// if (user && user.deletedAt) {
// return {
// withdrawnUser: true,
// providerId,
// };
// }

//μ‹ κ·œ μ‚¬μš©μž 생성
if (!user) {
Expand Down Expand Up @@ -196,4 +193,50 @@ export class KakaoAuthService {
// 이미 만료된 κ²½μš°λ„ λ‘œκ·Έμ•„μ›ƒμ€ 성곡 처리
}
}
}

async restoreKakaoUser(restoreToken) {
if (!restoreToken) {
throw new BadRequestError("TOKEN_REQUIRED", "볡ꡬ 토큰이 ν•„μš”ν•©λ‹ˆλ‹€.");
}

const providerId = await redis.get(`restore_token:${restoreToken}`);

if (!providerId) {
throw new BadRequestError("INVALID_TOKEN", "μœ νš¨ν•˜μ§€ μ•Šκ±°λ‚˜ 만료된 ν† ν°μž…λ‹ˆλ‹€.");
}

const user = await prisma.user.findFirst({
where: {
provider: "KAKAO",
providerId,
deletedAt: { not: null },
},
});

if (!user) {
throw new BadRequestError("USER_NOT_FOUND", "볡ꡬ할 μ‚¬μš©μžκ°€ μ—†μŠ΅λ‹ˆλ‹€.");
}

// 계정 볡ꡬ
const restoredUser = await prisma.user.update({
where: { id: user.id },
data: { deletedAt: null },
});

// 1νšŒμ„± 토큰 μ‚­μ œ
await redis.del(`restore_token:${restoreToken}`);

// 둜그인 토큰 λ°œκΈ‰
const accessToken = this.generateAccessToken(restoredUser);
const { refreshToken, tokenId } = this.generateRefreshToken(restoredUser);

await this.saveRefreshToken(tokenId, restoredUser.id);

return {
user: restoredUser,
accessToken,
refreshToken,
};
}
}