From 4d63ff818b8556334806cdd8787df44edf34e926 Mon Sep 17 00:00:00 2001 From: static Date: Wed, 22 Jan 2025 17:13:06 +0900 Subject: [PATCH] Refactor: ensure user is logged out immediately upon withdrawal --- src/modules/auths/login.ts | 9 ++++++++- src/routes/auth.js | 5 +---- src/services/auth.js | 21 +++++++++++---------- src/services/users.ts | 14 ++++++++++++-- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/modules/auths/login.ts b/src/modules/auths/login.ts index 962c260f..fe662503 100644 --- a/src/modules/auths/login.ts +++ b/src/modules/auths/login.ts @@ -1,6 +1,13 @@ import type { Request } from "express"; -import { session as sessionConfig } from "@/loadenv"; +import { session as sessionConfig, sparcssso as sparcsssoEnv } from "@/loadenv"; import logger from "@/modules/logger"; +import SsoClient from "./sparcssso"; + +// 환경변수 SPARCSSSO_CLIENT_ID 유무에 따라 로그인 방식이 변경됩니다. +export const isAuthReplace = !sparcsssoEnv.id; +export const ssoClient = !isAuthReplace + ? new SsoClient(sparcsssoEnv.id, sparcsssoEnv.key) + : undefined; export interface LoginInfo { id: string; diff --git a/src/routes/auth.js b/src/routes/auth.js index 534814f6..15fe76f0 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -6,10 +6,7 @@ const validator = require("@/middlewares/validator").default; const authHandlers = require("@/services/auth"); const authReplaceHandlers = require("@/services/auth.replace"); const mobileAuthHandlers = require("@/services/auth.mobile"); - -// 환경변수 SPARCSSSO_CLIENT_ID 유무에 따라 로그인 방식이 변경됩니다. -const { sparcssso: sparcsssoEnv } = require("@/loadenv"); -const isAuthReplace = !sparcsssoEnv?.id; +const { isAuthReplace } = require("@/modules/auths/login"); // 로그인 페이지로 redirect합니다. router.get( diff --git a/src/services/auth.js b/src/services/auth.js index 298ed9a7..b6e15fd2 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -1,7 +1,12 @@ -const { sparcssso: sparcsssoEnv, nodeEnv, testAccounts } = require("@/loadenv"); +const { nodeEnv, testAccounts } = require("@/loadenv"); const { userModel } = require("@/modules/stores/mongo"); const { user: userPattern } = require("@/modules/patterns").default; -const { getLoginInfo, logout, login } = require("@/modules/auths/login"); +const { + ssoClient, + getLoginInfo, + logout, + login, +} = require("@/modules/auths/login"); const { unregisterDeviceToken } = require("@/modules/fcm"); const { @@ -12,10 +17,6 @@ const { const jwt = require("@/modules/auths/jwt"); const logger = require("@/modules/logger").default; -// SPARCS SSO -const Client = require("@/modules/auths/sparcssso"); -const client = new Client(sparcsssoEnv?.id, sparcsssoEnv?.key); - const transUserData = (userData) => { const kaistInfo = userData.kaist_info ? JSON.parse(userData.kaist_info) : {}; @@ -137,7 +138,7 @@ const tryLogin = async (req, res, userData, redirectOrigin, redirectPath) => { const sparcsssoHandler = (req, res) => { const redirectPath = decodeURIComponent(req.query?.redirect || "%2F"); const isApp = !!req.query.isApp; - const { url, state } = client.getLoginParams(); + const { url, state } = ssoClient.getLoginParams(); req.session.loginAfterState = { state: state, @@ -167,7 +168,7 @@ const sparcsssoCallbackHandler = (req, res) => { return res.redirect(redirectUrl); } - client.getUserInfo(code).then((userDataBefore) => { + ssoClient.getUserInfo(code).then((userDataBefore) => { const userData = transUserData(userDataBefore); const isTestAccount = testAccounts?.includes(userData.email); if (userData.isEligible || nodeEnv !== "production" || isTestAccount) { @@ -176,7 +177,7 @@ const sparcsssoCallbackHandler = (req, res) => { // 카이스트 구성원이 아닌 경우, SSO 로그아웃 이후, 로그인 실패 URI 로 이동합니다 const { sid } = userData; const redirectUrl = new URL("/login/fail", redirectOrigin).href; - const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); + const ssoLogoutUrl = ssoClient.getLogoutUrl(sid, redirectUrl); res.redirect(ssoLogoutUrl); } }); @@ -202,7 +203,7 @@ const logoutHandler = async (req, res) => { // 로그아웃 URL을 생성 및 반환 const redirectUrl = new URL(redirectPath, req.origin).href; - const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); + const ssoLogoutUrl = ssoClient.getLogoutUrl(sid, redirectUrl); logout(req, res); res.json({ ssoLogoutUrl }); } catch (e) { diff --git a/src/services/users.ts b/src/services/users.ts index 7811bfe9..88d7f9c6 100644 --- a/src/services/users.ts +++ b/src/services/users.ts @@ -1,4 +1,5 @@ import type { RequestHandler } from "express"; +import { ssoClient, getLoginInfo, logout } from "@/modules/auths/login"; import { unregisterAllDeviceTokens } from "@/modules/fcm"; import logger from "@/modules/logger"; import { @@ -257,6 +258,11 @@ export const getBanRecordHandler: RequestHandler = async (req, res) => { export const withdrawHandler: RequestHandler = async (req, res) => { try { + const redirectPath = decodeURIComponent( + (req.query.redirect as string | undefined) || "%2F" + ); // TODO: Typing or Validation + const { sid } = getLoginInfo(req); + const user = await userModel.findOne({ _id: req.userOid, withdraw: false }); if (!user) { return res.status(500).send("Users/withdraw : internal server error"); @@ -275,10 +281,14 @@ export const withdrawHandler: RequestHandler = async (req, res) => { // 회원 탈퇴 처리 (Soft Delete) user.withdraw = true; user.withdrewAt = new Date(req.timestamp!); - await user.save(); - res.status(200).send("Users/withdraw : withdraw successful"); + // 로그아웃 처리 + const redirectUrl = new URL(redirectPath, req.origin).href; + const ssoLogoutUrl = + ssoClient?.getLogoutUrl(sid, redirectUrl) ?? redirectUrl; + logout(req); + res.json({ ssoLogoutUrl }); } catch (err) { res.status(500).send("Users/withdraw : internal server error"); }