From 1f1a71648146438d44eae6afe556b053a038d457 Mon Sep 17 00:00:00 2001 From: jjikky Date: Sat, 22 Jun 2024 03:05:21 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=90=9B=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=ED=97=A4=EB=8D=94=20=EA=B0=92=20?= =?UTF-8?q?=EC=9E=98=EB=AA=BB=EC=9D=BD=EC=96=B4=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/utils/kakao.js | 64 +++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/common/utils/kakao.js b/src/common/utils/kakao.js index 00db064..9c57ee6 100644 --- a/src/common/utils/kakao.js +++ b/src/common/utils/kakao.js @@ -1,37 +1,49 @@ const axios = require('axios'); const conf = require('../../common/config'); -const header = { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: 'Bearer ', -}; exports.getKakaoToken = async (code) => { - const data = { - grant_type: 'authorization_code', - client_id: conf.kakaoRestApiKey, - code, - }; + try { + const header = { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + Authorization: 'Bearer ', + }; + const data = { + grant_type: 'authorization_code', + client_id: conf.kakaoRestApiKey, + code, + }; - const queryString = Object.keys(data) - .map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(data[k])) - .join('&'); + const queryString = Object.keys(data) + .map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(data[k])) + .join('&'); - const token = await axios.post('https://kauth.kakao.com/oauth/token', queryString, { headers: header }); - return { accessToken: token.data.access_token }; + const token = await axios.post('https://kauth.kakao.com/oauth/token', queryString, { headers: header }); + return { accessToken: token.data.access_token }; + } catch (err) { + console.log(err); + } }; exports.getUserInfo = async (accessToken) => { - // Authorization: 'Bearer access_token' - header.Authorization += accessToken; + try { + const header = { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + Authorization: 'Bearer ', + }; + // Authorization: 'Bearer access_token' + header.Authorization += accessToken; - // 카카오 사용자 정보 조회 - const get = await axios.get('https://kapi.kakao.com/v2/user/me', { headers: header }); - const result = get.data; + // 카카오 사용자 정보 조회 + const get = await axios.get('https://kapi.kakao.com/v2/user/me', { headers: header }); + const result = get.data; - return { - snsId: result.id, - email: result.kakao_account.email ? result.kakao_account.email : `${result.id}@no.agreement`, - // NOTE: 닉네임 10글자 제한 때문에, 임시 처리 - // kakao 닉네임 규정은 20글자. result.id는 10글자로 추정 - nickname: result.id, - }; + return { + snsId: result.id, + email: result.kakao_account.email ? result.kakao_account.email : `${result.id}@no.agreement`, + // NOTE: 닉네임 10글자 제한 때문에, 임시 처리 + // kakao 닉네임 규정은 20글자. result.id는 10글자로 추정 + nickname: result.id, + }; + } catch (err) { + console.log(err); + } }; From 9ebbc2f2bb3cddab1d28e20711dfc62cbc593cac Mon Sep 17 00:00:00 2001 From: jjikky Date: Sat, 22 Jun 2024 03:06:31 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20jwt=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=20=EC=9C=A0=ED=8B=B8=EB=A6=AC=ED=8B=B0=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/passport/jwtStrategy.js | 2 +- src/common/utils/auth.js | 75 ++++++++++++++++++------------ 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/common/passport/jwtStrategy.js b/src/common/passport/jwtStrategy.js index ae1c388..8bd81a8 100644 --- a/src/common/passport/jwtStrategy.js +++ b/src/common/passport/jwtStrategy.js @@ -1,7 +1,7 @@ const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt'); const passport = require('passport'); const User = require('../../routes/user/user.model'); -const config = require('../config'); // 비밀 키를 저장한 파일 +const config = require('../config'); const opts = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), diff --git a/src/common/utils/auth.js b/src/common/utils/auth.js index d3875e7..0e5ac19 100644 --- a/src/common/utils/auth.js +++ b/src/common/utils/auth.js @@ -2,6 +2,7 @@ const jwt = require('jsonwebtoken'); const passport = require('passport'); const config = require('../config'); +// JWT 토큰 생성 함수 exports.generateToken = (user) => { return jwt.sign( { @@ -14,44 +15,60 @@ exports.generateToken = (user) => { ); }; +// JWT 토큰 검증 함수 exports.verifyToken = (token) => { return jwt.verify(token, config.jwtSecret); }; -exports.isAuthenticated = (req, res, next) => { - passport.authenticate('jwt', { session: false }, (err, user, info) => { - if (err) { - return res.status(500).json({ message: '서버 오류' }); - } - if (!user) { - return res.status(401).json({ message: '인증되지 않은 사용자' }); - } - req.user = user; - next(); - })(req, res, next); +// JWT 인증 함수 +const authenticateJWT = (req, res) => { + return new Promise((resolve, reject) => { + passport.authenticate('jwt', { session: false }, (err, user, info) => { + if (err) { + return reject({ status: 500, message: '서버 오류', error: err.message }); + } + if (info) { + switch (info.name) { + case 'TokenExpiredError': + return reject({ status: 401, message: '토큰이 만료되었습니다.', expiredAt: info.expiredAt }); + case 'JsonWebTokenError': + return reject({ status: 401, message: '유효하지 않은 토큰입니다.' }); + default: + return reject({ status: 401, message: '인증되지 않은 사용자' }); + } + } + if (!user) { + return reject({ status: 401, message: '로그인이 필요합니다.' }); + } + resolve(user); + })(req, res); + }); }; -exports.isLoggedIn = (req, res, next) => { - passport.authenticate('jwt', { session: false }, (err, user, info) => { - if (err) { - return res.status(500).json({ message: '서버 오류' }); - } - if (!user) { - return res.status(401).json({ message: '로그인이 필요합니다.' }); - } +// 로그인 여부 확인 미들웨어 +exports.isLoggedIn = async (req, res, next) => { + try { + const user = await authenticateJWT(req, res); req.user = user; next(); - })(req, res, next); + } catch (error) { + res.status(error.status).json({ + message: error.message, + ...(error.expiredAt && { expiredAt: error.expiredAt }), + }); + } }; -exports.isNotLoggedIn = (req, res, next) => { - passport.authenticate('jwt', { session: false }, (err, user, info) => { - if (err) { - return res.status(500).json({ message: '서버 오류' }); +// 비로그인 여부 확인 미들웨어 +exports.isNotLoggedIn = async (req, res, next) => { + try { + await authenticateJWT(req, res); + res.status(403).json({ message: '이미 로그인된 상태입니다.' }); + } catch (error) { + if (error.status === 401) { + next(); + } else { + res.status(error.status).json({ message: error.message }); } - if (user) { - return res.status(403).json({ message: '이미 로그인된 상태입니다.' }); - } - next(); - })(req, res, next); + } };