From 02e9f8c0ab5157f8f306dff7f5a6ac8a7c93277a Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Wed, 1 Apr 2026 13:34:49 +0900 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=B3=80=EA=B2=BD=20API=20=EC=B6=94=EA=B0=80=20+?= =?UTF-8?q?=20=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EB=B0=94=EB=A1=9C?= =?UTF-8?q?=EA=B0=80=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POST /api/auth/change-password: 현재 비밀번호 검증 후 변경 - POST /api/auth/check-password-strength: 비밀번호 강도 체크 - 대시보드 프로필 카드에 '비밀번호 변경' 바로가기 링크 추가 - 프론트엔드(password.html + change-password.js)는 이미 구현됨 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../controllers/authController.js | 63 ++++++++++++++++++- sso-auth-service/routes/authRoutes.js | 4 ++ .../web/js/production-dashboard.js | 4 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/sso-auth-service/controllers/authController.js b/sso-auth-service/controllers/authController.js index 00b7d6e..879d197 100644 --- a/sso-auth-service/controllers/authController.js +++ b/sso-auth-service/controllers/authController.js @@ -366,6 +366,65 @@ async function deleteUser(req, res, next) { } } +/** + * POST /api/auth/change-password — 본인 비밀번호 변경 + */ +async function changePassword(req, res, next) { + try { + const token = extractToken(req); + if (!token) return res.status(401).json({ success: false, message: '인증이 필요합니다' }); + + const decoded = jwt.verify(token, JWT_SECRET, { algorithms: ['HS256'] }); + const userId = decoded.user_id || decoded.id; + const user = await userModel.findById(userId); + if (!user || !user.is_active) { + return res.status(401).json({ success: false, message: '유효하지 않은 사용자입니다' }); + } + + const { currentPassword, newPassword } = req.body; + if (!currentPassword || !newPassword) { + return res.status(400).json({ success: false, message: '현재 비밀번호와 새 비밀번호를 모두 입력해주세요' }); + } + if (newPassword.length < 6) { + return res.status(400).json({ success: false, message: '새 비밀번호는 6자 이상이어야 합니다' }); + } + if (currentPassword === newPassword) { + return res.status(400).json({ success: false, message: '새 비밀번호는 현재 비밀번호와 달라야 합니다' }); + } + + const isValid = await userModel.verifyPassword(currentPassword, user.password_hash); + if (!isValid) { + return res.status(400).json({ success: false, message: '현재 비밀번호가 올바르지 않습니다' }); + } + + await userModel.update(userId, { password: newPassword }); + res.json({ success: true, message: '비밀번호가 변경되었습니다. 다시 로그인해주세요.' }); + } catch (err) { + if (err.name === 'JsonWebTokenError' || err.name === 'TokenExpiredError') { + return res.status(401).json({ success: false, message: '인증이 만료되었습니다' }); + } + next(err); + } +} + +/** + * POST /api/auth/check-password-strength — 비밀번호 강도 체크 + */ +async function checkPasswordStrength(req, res) { + const { password } = req.body; + if (!password) return res.json({ success: true, data: { score: 0, level: 'weak' } }); + + let score = 0; + if (password.length >= 6) score++; + if (password.length >= 8) score++; + if (/[A-Z]/.test(password)) score++; + if (/[0-9]/.test(password)) score++; + if (/[^A-Za-z0-9]/.test(password)) score++; + + const level = score <= 1 ? 'weak' : score <= 3 ? 'medium' : 'strong'; + res.json({ success: true, data: { score, level } }); +} + /** * Bearer 토큰 또는 쿠키에서 토큰 추출 */ @@ -388,5 +447,7 @@ module.exports = { getUsers, createUser, updateUser, - deleteUser + deleteUser, + changePassword, + checkPasswordStrength }; diff --git a/sso-auth-service/routes/authRoutes.js b/sso-auth-service/routes/authRoutes.js index a3297fb..fa117c5 100644 --- a/sso-auth-service/routes/authRoutes.js +++ b/sso-auth-service/routes/authRoutes.js @@ -33,6 +33,10 @@ router.get('/me', authController.me); router.post('/refresh', authController.refresh); router.post('/logout', authController.logout); +// 인증 사용자 엔드포인트 +router.post('/change-password', authController.changePassword); +router.post('/check-password-strength', authController.checkPasswordStrength); + // 관리자 엔드포인트 router.get('/users', requireAdmin, authController.getUsers); router.post('/users', requireAdmin, authController.createUser); diff --git a/system1-factory/web/js/production-dashboard.js b/system1-factory/web/js/production-dashboard.js index 0c3986d..badb81f 100644 --- a/system1-factory/web/js/production-dashboard.js +++ b/system1-factory/web/js/production-dashboard.js @@ -92,7 +92,9 @@ function renderDashboard(data) {
${escHtml(initial)}
${escHtml(user.worker_name || user.name)}
-
${escHtml(user.job_type || '')}${user.job_type ? ' · ' : ''}${escHtml(user.department_name)}
+
${escHtml(user.job_type || '')}${user.job_type ? ' · ' : ''}${escHtml(user.department_name)} + 비밀번호 변경 +