diff --git a/.gitignore b/.gitignore index 82d85fc..e314ca7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ __pycache__/ *.pyc +*.bak* .env *.tar.gz uploads/ diff --git a/gateway/html/login.html b/gateway/html/login.html index ccd0ba3..af399cb 100644 --- a/gateway/html/login.html +++ b/gateway/html/login.html @@ -161,7 +161,12 @@ // redirect 파라미터가 있으면 해당 URL로, 없으면 포털로 var redirect = new URLSearchParams(location.search).get('redirect'); - window.location.href = redirect || '/'; + // Open redirect 방지: 상대 경로 또는 같은 도메인만 허용 + if (redirect && (redirect.startsWith('/') && !redirect.startsWith('//')) && !redirect.includes('://')) { + window.location.href = redirect; + } else { + window.location.href = '/'; + } } catch (err) { errEl.textContent = err.message; errEl.style.display = ''; diff --git a/sso-auth-service/controllers/authController.js b/sso-auth-service/controllers/authController.js index d88b468..04c2ada 100644 --- a/sso-auth-service/controllers/authController.js +++ b/sso-auth-service/controllers/authController.js @@ -65,14 +65,8 @@ async function login(req, res, next) { { expiresIn: JWT_REFRESH_EXPIRES_IN } ); - // SSO 쿠키 설정 (동일 도메인 공유) - res.cookie('sso_token', access_token, { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', - maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days - path: '/' - }); + // SSO 쿠키는 클라이언트(login.html)에서 domain=.technicalkorea.net으로 설정 + // 서버 httpOnly 쿠키는 서브도메인 공유 불가하므로 제거 res.json({ success: true, @@ -237,14 +231,6 @@ async function refresh(req, res, next) { { expiresIn: JWT_REFRESH_EXPIRES_IN } ); - res.cookie('sso_token', access_token, { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', - maxAge: 7 * 24 * 60 * 60 * 1000, - path: '/' - }); - res.json({ success: true, access_token, @@ -335,15 +321,11 @@ async function deleteUser(req, res, next) { * Bearer 토큰 또는 쿠키에서 토큰 추출 */ function extractToken(req) { - // Authorization header + // Authorization header (SSO 토큰은 항상 Bearer로 전달) const authHeader = req.headers['authorization']; if (authHeader && authHeader.startsWith('Bearer ')) { return authHeader.split(' ')[1]; } - // Cookie - if (req.cookies && req.cookies.sso_token) { - return req.cookies.sso_token; - } return null; } diff --git a/system2-report/api/index.js b/system2-report/api/index.js index 4f9d8e8..6f270e7 100644 --- a/system2-report/api/index.js +++ b/system2-report/api/index.js @@ -45,31 +45,8 @@ app.get('/api/health', (req, res) => { res.json({ status: 'ok', service: 'system2-report', timestamp: new Date().toISOString() }); }); -// JWT Auth middleware (SSO 공유 시크릿) -const jwt = require('jsonwebtoken'); -const requireAuth = (req, res, next) => { - try { - const authHeader = req.headers['authorization']; - if (!authHeader) { - throw new AuthenticationError('Authorization 헤더가 필요합니다'); - } - const token = authHeader.split(' ')[1]; - if (!token) { - throw new AuthenticationError('Bearer 토큰이 필요합니다'); - } - const decoded = jwt.verify(token, process.env.JWT_SECRET); - req.user = decoded; - next(); - } catch (err) { - if (err.name === 'JsonWebTokenError' || err.name === 'TokenExpiredError') { - return res.status(401).json({ success: false, error: '유효하지 않은 토큰입니다' }); - } - if (err instanceof AuthenticationError) { - return res.status(401).json({ success: false, error: err.message }); - } - next(err); - } -}; +// JWT Auth middleware (middlewares/auth.js 사용) +const { requireAuth } = require('./middlewares/auth'); // Routes const workIssueRoutes = require('./routes/workIssueRoutes'); @@ -77,6 +54,11 @@ const workIssueRoutes = require('./routes/workIssueRoutes'); // 인증이 필요한 API app.use('/api/work-issues', requireAuth, workIssueRoutes); +// 404 (에러 핸들러보다 먼저 등록) +app.use((req, res) => { + res.status(404).json({ success: false, error: 'Not Found', path: req.originalUrl }); +}); + // Error handler app.use((err, req, res, next) => { const statusCode = err.statusCode || 500; @@ -87,11 +69,6 @@ app.use((err, req, res, next) => { }); }); -// 404 -app.use((req, res) => { - res.status(404).json({ success: false, error: 'Not Found', path: req.originalUrl }); -}); - app.listen(PORT, () => { console.log(`System 2 (신고) API running on port ${PORT}`); });