From 4cece2ce1b301f9f031b12fbf4e547bb01c224c9 Mon Sep 17 00:00:00 2001 From: jjikky Date: Wed, 19 Jun 2024 23:08:43 +0900 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=94=87=20userSchema=20TODO=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/user/user.model.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/routes/user/user.model.js b/src/routes/user/user.model.js index 24076a3..d385e50 100644 --- a/src/routes/user/user.model.js +++ b/src/routes/user/user.model.js @@ -2,9 +2,6 @@ const mongoose = require('mongoose'); const userSchema = new mongoose.Schema( { - // TODO : 동작 확인 후 주석 삭제 - // createdAt: { type: Date, default: Date.now }, - // updatedAt: { type: Date, default: Date.now }, deletedAt: { type: Date, default: null }, nickname: { type: String, required: true, unique: true }, email: { type: String, required: true, unique: true }, From 06534779c18116b967a1c8bd9d8095fd6baabc8e Mon Sep 17 00:00:00 2001 From: jjikky Date: Wed, 19 Jun 2024 23:57:25 +0900 Subject: [PATCH 2/9] =?UTF-8?q?=E2=9E=95=20ajv=20format=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 25 +++++++++++++++++++++++++ package.json | 1 + 2 files changed, 26 insertions(+) diff --git a/package-lock.json b/package-lock.json index 5d825be..79c22e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "ajv": "^8.16.0", + "ajv-formats": "^3.0.1", "bcrypt": "^5.1.1", "cookie-parser": "^1.4.6", "cors": "^2.8.5", @@ -470,6 +471,22 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/amp": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", @@ -5737,6 +5754,14 @@ "uri-js": "^4.4.1" } }, + "ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "requires": { + "ajv": "^8.0.0" + } + }, "amp": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", diff --git a/package.json b/package.json index ddb1dc0..56d080f 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "homepage": "https://github.com/Murakano/murakano-be#readme", "dependencies": { "ajv": "^8.16.0", + "ajv-formats": "^3.0.1", "bcrypt": "^5.1.1", "cookie-parser": "^1.4.6", "cors": "^2.8.5", From f8807bdf36fead7620fbc322b30024737daa6bb5 Mon Sep 17 00:00:00 2001 From: jjikky Date: Wed, 19 Jun 2024 23:58:04 +0900 Subject: [PATCH 3/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85,=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=B2=B4=ED=81=AC=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/utils/request.validator.js | 2 ++ src/routes/user/user.controller.js | 22 ++++++++++----- src/routes/user/user.schema.js | 39 ++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/common/utils/request.validator.js b/src/common/utils/request.validator.js index 7ede557..86e7d54 100644 --- a/src/common/utils/request.validator.js +++ b/src/common/utils/request.validator.js @@ -1,6 +1,8 @@ const ErrorMessage = require('../../common/constants/error-message'); const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); const ajv = new Ajv(); +addFormats(ajv); exports.validateRequest = (schema, req) => { const validate = ajv.compile(schema); diff --git a/src/routes/user/user.controller.js b/src/routes/user/user.controller.js index 13f6aba..db21b95 100644 --- a/src/routes/user/user.controller.js +++ b/src/routes/user/user.controller.js @@ -3,19 +3,23 @@ const sendResponse = require('../../common/utils/response-handler'); const ErrorMessage = require('../../common/constants/error-message'); const SucesssMessage = require('../../common/constants/success-message'); const { validateRequest } = require('../../common/utils/request.validator'); -const { nicknameCheckReqQuerySchema } = require('./user.schema'); +const { nicknameCheckReqQuerySchema, registerBodySchema, emailCheckReqQuerySchema } = require('./user.schema'); exports.register = async (req, res) => { - // TODO : validation 적용 try { - const newUser = await userService.register(req.body); + const validData = validateRequest(registerBodySchema, req.body); + const newUser = await userService.register(validData); - data = { user_id: newUser._id }; + const data = { user_id: newUser._id }; sendResponse.created(res, { message: SucesssMessage.REGISTER_SUCCESSS, data, }); } catch (err) { + console.log(err); + if (err?.type) { + return sendResponse.badRequest(res, err.message); + } sendResponse.fail(req, res, ErrorMessage.REGISTER_ERROR); } }; @@ -47,10 +51,11 @@ exports.isNicknameExist = async (req, res) => { }; exports.isEmailExist = async (req, res) => { - // TODO : validation 적용 try { - const isUserExist = await userService.isEmailExist(req.query.email); - data = { isUserExist }; + const { email } = validateRequest(emailCheckReqQuerySchema, req.query); + + const isUserExist = await userService.isEmailExist(email); + const data = { isUserExist }; if (isUserExist) { return sendResponse.badRequest(res, { @@ -63,6 +68,9 @@ exports.isEmailExist = async (req, res) => { data, }); } catch (err) { + if (err?.type) { + return sendResponse.badRequest(res, err.message); + } sendResponse.fail(req, res, ErrorMessage.EMAIL_CHECK_ERROR); } }; diff --git a/src/routes/user/user.schema.js b/src/routes/user/user.schema.js index b4e0d75..469f5d2 100644 --- a/src/routes/user/user.schema.js +++ b/src/routes/user/user.schema.js @@ -1,3 +1,27 @@ +const registerBodySchema = { + type: 'object', + properties: { + nickname: { + type: 'string', + maxLength: 10, + pattern: '^[a-zA-Z0-9가-힣]+$', + }, + email: { + type: 'string', + format: 'email', + maxLength: 50, + }, + password: { + type: 'string', + minLength: 8, + maxLength: 20, + pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};:\'",.<>\\/\\\\?]).+$', + }, + }, + required: ['nickname', 'email', 'password'], + additionalProperties: false, +}; + const nicknameCheckReqQuerySchema = { type: 'object', properties: { @@ -11,4 +35,17 @@ const nicknameCheckReqQuerySchema = { additionalProperties: false, }; -module.exports = { nicknameCheckReqQuerySchema }; +const emailCheckReqQuerySchema = { + type: 'object', + properties: { + email: { + type: 'string', + format: 'email', + maxLength: 50, + }, + }, + required: ['email'], + additionalProperties: false, +}; + +module.exports = { registerBodySchema, nicknameCheckReqQuerySchema, emailCheckReqQuerySchema }; From 372c65a8157c5eaed0c7b7a010432ca80380aa21 Mon Sep 17 00:00:00 2001 From: jjikky Date: Thu, 20 Jun 2024 01:39:31 +0900 Subject: [PATCH 4/9] =?UTF-8?q?=E2=9E=95=20jwt=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=A2=85=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 309 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 + 2 files changed, 312 insertions(+) diff --git a/package-lock.json b/package-lock.json index 79c22e8..27369ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,12 +17,15 @@ "crypto": "^1.0.1", "dotenv": "^16.4.5", "express": "^4.19.2", + "express-session": "^1.18.0", "helmet": "^7.1.0", + "jsonwebtoken": "^9.0.2", "moment": "^2.30.1", "mongoose": "^8.4.3", "morgan": "^1.10.0", "nodemon": "^3.1.0", "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "pm2": "^5.3.1" }, @@ -860,6 +863,11 @@ "node": ">=16.20.1" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -1305,6 +1313,14 @@ "url": "https://dotenvx.com" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1990,6 +2006,50 @@ "node": ">= 0.10.0" } }, + "node_modules/express-session": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", + "dependencies": { + "cookie": "0.6.0", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/express/node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -3070,6 +3130,46 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", @@ -3128,12 +3228,47 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -3920,6 +4055,15 @@ "url": "https://github.com/sponsors/jaredhanson" } }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, "node_modules/passport-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", @@ -4291,6 +4435,14 @@ } ] }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -5192,6 +5344,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -6045,6 +6208,11 @@ "resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz", "integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6368,6 +6536,14 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -6904,6 +7080,46 @@ } } }, + "express-session": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", + "requires": { + "cookie": "0.6.0", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + }, + "cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, "extrareqp2": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/extrareqp2/-/extrareqp2-1.0.0.tgz", @@ -7684,6 +7900,42 @@ "universalify": "^2.0.0" } }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kareem": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", @@ -7727,12 +7979,47 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -8289,6 +8576,15 @@ "utils-merge": "^1.0.1" } }, + "passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "requires": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, "passport-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", @@ -8563,6 +8859,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==" + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -9199,6 +9500,14 @@ "possible-typed-array-names": "^1.0.0" } }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index 56d080f..71341a1 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,15 @@ "crypto": "^1.0.1", "dotenv": "^16.4.5", "express": "^4.19.2", + "express-session": "^1.18.0", "helmet": "^7.1.0", + "jsonwebtoken": "^9.0.2", "moment": "^2.30.1", "mongoose": "^8.4.3", "morgan": "^1.10.0", "nodemon": "^3.1.0", "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "pm2": "^5.3.1" }, From 352f6b26c44355808076e42fd852a269900a06fd Mon Sep 17 00:00:00 2001 From: jjikky Date: Thu, 20 Jun 2024 02:13:03 +0900 Subject: [PATCH 5/9] =?UTF-8?q?=E2=9C=A8=20login=20(=20+=20jwt=20=20)=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/config/index.js | 2 + src/common/constants/success-message.js | 5 +- src/common/modules/express/index.js | 28 ++++++++++- src/common/passport/index.js | 21 +++++++++ src/common/passport/jwtStrategy.js | 27 +++++++++++ src/common/passport/localStrategy.js | 30 ++++++++++++ src/common/utils/auth.js | 57 +++++++++++++++++++++++ src/routes/user/user.controller.js | 55 +++++++++++++++++++++- src/routes/user/user.model.js | 27 +++++++++-- src/routes/user/user.route.js | 14 +++++- src/routes/user/user.schema.js | 62 ++++++++++++++----------- 11 files changed, 291 insertions(+), 37 deletions(-) create mode 100644 src/common/passport/index.js create mode 100644 src/common/passport/jwtStrategy.js create mode 100644 src/common/passport/localStrategy.js create mode 100644 src/common/utils/auth.js diff --git a/src/common/config/index.js b/src/common/config/index.js index 4b09151..dcedc7d 100644 --- a/src/common/config/index.js +++ b/src/common/config/index.js @@ -12,6 +12,8 @@ const conf = { port: process.env.PORT, corsWhiteList: process.env.CORS_WHITELIST, mongoURL: process.env.MONGO_URL, + jwtSecret: process.env.JWT_SECRET, + cookieSecret: process.env.COOKIE_SECRET, }; module.exports = conf; diff --git a/src/common/constants/success-message.js b/src/common/constants/success-message.js index 9ec1f03..944f7a6 100644 --- a/src/common/constants/success-message.js +++ b/src/common/constants/success-message.js @@ -1,8 +1,11 @@ const SucesssMessage = Object.freeze({ - // USER + // USER - 회원가입 REGISTER_SUCCESSS: '회원가입 성공', AVAILABLE_NICKNAME: '사용 가능한 닉네임입니다.', AVAILABLE_EMAIL: '사용 가능한 이메일입니다.', + + // USER - 로그인 + LOGIN_SUCCESSS: '로그인 성공', }); module.exports = SucesssMessage; diff --git a/src/common/modules/express/index.js b/src/common/modules/express/index.js index 7343c0f..665f2f4 100644 --- a/src/common/modules/express/index.js +++ b/src/common/modules/express/index.js @@ -1,14 +1,19 @@ const express = require('express'); const morgan = require('morgan'); +const passport = require('passport'); +const session = require('express-session'); const helmet = require('helmet'); const cors = require('cors'); const crypto = require('crypto'); const cookieParser = require('cookie-parser'); -const conf = require('../../config/index'); +const conf = require('../../config'); +const passportConfig = require('../../passport'); const router = require('../../../routes/index'); module.exports = expressLoader = (app) => { + passportConfig(); + app.use(morgan('dev')); app.use(helmet()); @@ -31,6 +36,27 @@ module.exports = expressLoader = (app) => { })(req, res, next); }); + app.use( + session({ + name: 'user', + resave: false, + saveUninitialized: false, + secret: conf.cookieSecret, + cookie: { + // cleint 쿠키 접근 불가 + httpOnly: true, + // TODO : ssl 적용하면 true로 변경 + secure: false, + // 24h + maxAge: 86400000, + }, + }) + ); + + // Passport 세팅 + app.use(passport.initialize()); + app.use(passport.session()); + // Body Parser 세팅 app.use( express.json({ diff --git a/src/common/passport/index.js b/src/common/passport/index.js new file mode 100644 index 0000000..884a1f5 --- /dev/null +++ b/src/common/passport/index.js @@ -0,0 +1,21 @@ +const passport = require('passport'); +const local = require('./localStrategy'); +const jwtStrategy = require('./jwtStrategy'); + +module.exports = () => { + passport.serializeUser((user, done) => { + done(null, user.id); + }); + + passport.deserializeUser(async (id, done) => { + try { + const user = await User.findById(id); + done(null, user); + } catch (err) { + done(err); + } + }); + + local(); // LocalStrategy 초기화 + jwtStrategy(); // JWTStrategy 초기화 +}; diff --git a/src/common/passport/jwtStrategy.js b/src/common/passport/jwtStrategy.js new file mode 100644 index 0000000..ae1c388 --- /dev/null +++ b/src/common/passport/jwtStrategy.js @@ -0,0 +1,27 @@ +const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt'); +const passport = require('passport'); +const User = require('../../routes/user/user.model'); +const config = require('../config'); // 비밀 키를 저장한 파일 + +const opts = { + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + secretOrKey: config.jwtSecret, +}; + +module.exports = () => { + passport.use( + new JwtStrategy(opts, async (jwtPayload, done) => { + try { + const user = await User.findById(jwtPayload.id); + if (user) { + return done(null, user); + } else { + return done(null, false); + } + } catch (error) { + console.error(error); + return done(error, false); + } + }) + ); +}; diff --git a/src/common/passport/localStrategy.js b/src/common/passport/localStrategy.js new file mode 100644 index 0000000..5f2bb0b --- /dev/null +++ b/src/common/passport/localStrategy.js @@ -0,0 +1,30 @@ +const passport = require('passport'); +const LocalStrategy = require('passport-local').Strategy; +const User = require('../../routes/user/user.model'); + +module.exports = () => { + passport.use( + new LocalStrategy( + { + usernameField: 'email', + passwordField: 'password', + }, + async (email, password, done) => { + try { + const user = await User.findOne({ email }); + if (!user) { + return done(null, false, { message: '가입되지 않은 회원입니다.' }); + } + const isMatch = await user.comparePassword(password); + if (!isMatch) { + return done(null, false, { message: '비밀번호가 일치하지 않습니다.' }); + } + return done(null, user); + } catch (error) { + console.error(error); + return done(error); + } + } + ) + ); +}; diff --git a/src/common/utils/auth.js b/src/common/utils/auth.js new file mode 100644 index 0000000..d3875e7 --- /dev/null +++ b/src/common/utils/auth.js @@ -0,0 +1,57 @@ +const jwt = require('jsonwebtoken'); +const passport = require('passport'); +const config = require('../config'); + +exports.generateToken = (user) => { + return jwt.sign( + { + id: user._id, + email: user.email, + nickname: user.nickname, + }, + config.jwtSecret, + { expiresIn: '24h' } + ); +}; + +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); +}; + +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: '로그인이 필요합니다.' }); + } + req.user = user; + next(); + })(req, res, next); +}; + +exports.isNotLoggedIn = (req, res, next) => { + passport.authenticate('jwt', { session: false }, (err, user, info) => { + if (err) { + return res.status(500).json({ message: '서버 오류' }); + } + if (user) { + return res.status(403).json({ message: '이미 로그인된 상태입니다.' }); + } + next(); + })(req, res, next); +}; diff --git a/src/routes/user/user.controller.js b/src/routes/user/user.controller.js index db21b95..627f1cd 100644 --- a/src/routes/user/user.controller.js +++ b/src/routes/user/user.controller.js @@ -1,9 +1,19 @@ +const passport = require('passport'); +const jwt = require('jsonwebtoken'); + const userService = require('./user.service'); const sendResponse = require('../../common/utils/response-handler'); const ErrorMessage = require('../../common/constants/error-message'); const SucesssMessage = require('../../common/constants/success-message'); const { validateRequest } = require('../../common/utils/request.validator'); -const { nicknameCheckReqQuerySchema, registerBodySchema, emailCheckReqQuerySchema } = require('./user.schema'); +const { + nicknameCheckReqQuerySchema, + registerBodySchema, + emailCheckReqQuerySchema, + loginBodySchema, +} = require('./user.schema'); +const config = require('../../common/config'); +const { generateToken } = require('../../common/utils/auth'); exports.register = async (req, res) => { try { @@ -74,3 +84,46 @@ exports.isEmailExist = async (req, res) => { sendResponse.fail(req, res, ErrorMessage.EMAIL_CHECK_ERROR); } }; + +// const { email, password } = validateRequest(loginBodySchema, req.body); +// exports.localLogin = async (req, res) => { + +// passport.authenticate('local', (authError, user, info) => { +// if (authError) { +// console.error(authError); +// return next(authError); +// } +// if (!user) { +// return sendResponse.unAuthorized(res, { message: info.message }); +// } +// return req.login(user, (loginError) => { +// if (loginError) { +// console.error(loginError); +// return next(loginError); +// } +// return sendResponse.ok(res, { +// message: SucesssMessage.LOGIN_SUCCESSS, +// user_id: user._id, +// }); +// }); +// })(req, res, next); +// }; + +exports.localLogin = async (req, res, next) => { + passport.authenticate('local', (authError, user, info) => { + if (authError) { + console.error(authError); + return next(authError); + } + if (!user) { + return sendResponse.unAuthorized(res, { message: info.message }); + } + + const token = generateToken(user); + + return sendResponse.ok(res, { + message: SucesssMessage.LOGIN_SUCCESSS, + token, + }); + })(req, res, next); +}; diff --git a/src/routes/user/user.model.js b/src/routes/user/user.model.js index d385e50..b10b810 100644 --- a/src/routes/user/user.model.js +++ b/src/routes/user/user.model.js @@ -1,11 +1,18 @@ const mongoose = require('mongoose'); +const bcrypt = require('bcrypt'); const userSchema = new mongoose.Schema( { deletedAt: { type: Date, default: null }, - nickname: { type: String, required: true, unique: true }, - email: { type: String, required: true, unique: true }, - password: { type: String }, + nickname: { type: String, required: true, unique: true, maxlength: 10, match: /^[a-zA-Z0-9가-힣]+$/ }, + email: { + type: String, + required: true, + unique: true, + maxlength: 50, + match: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/, + }, + password: { type: String, minLength: 8, maxLength: 20 }, role: { type: String, default: 'user' }, snsId: { type: String, default: null }, provider: { type: String, default: null }, @@ -15,6 +22,16 @@ const userSchema = new mongoose.Schema( } ); -const model = mongoose.model('User', userSchema); +userSchema.pre('save', async function (next) { + if (this.isModified('password') || this.isNew) { + const salt = await bcrypt.genSalt(10); + this.password = await bcrypt.hash(this.password, salt); + } + next(); +}); + +userSchema.methods.comparePassword = async function (password) { + return bcrypt.compare(password, this.password); +}; -module.exports = model; +module.exports = mongoose.model('User', userSchema); diff --git a/src/routes/user/user.route.js b/src/routes/user/user.route.js index 20cd618..bbb9a2e 100644 --- a/src/routes/user/user.route.js +++ b/src/routes/user/user.route.js @@ -1,9 +1,19 @@ const express = require('express'); -const { register, isNicknameExist, isEmailExist } = require('./user.controller'); +const { register, isNicknameExist, isEmailExist, localLogin } = require('./user.controller'); +const { isLoggedIn, isNotLoggedIn } = require('../../common/utils/auth'); const userRouter = express.Router(); -userRouter.post('/register', register); +// 회원가입 +userRouter.post('/register', isNotLoggedIn, register); userRouter.get('/check/nickname', isNicknameExist); userRouter.get('/check/email', isEmailExist); +// 로그인 +userRouter.post('/local/login', isNotLoggedIn, localLogin); + +// NOTE : JWT 확인용. 삭제 예정 +userRouter.get('/profile', isLoggedIn, (req, res) => { + res.json({ user: req.user }); +}); + module.exports = userRouter; diff --git a/src/routes/user/user.schema.js b/src/routes/user/user.schema.js index 469f5d2..699e673 100644 --- a/src/routes/user/user.schema.js +++ b/src/routes/user/user.schema.js @@ -1,22 +1,28 @@ +const commonSchemas = { + nickname: { + type: 'string', + maxLength: 10, + pattern: '^[a-zA-Z0-9가-힣]+$', + }, + email: { + type: 'string', + format: 'email', + maxLength: 50, + }, + password: { + type: 'string', + minLength: 8, + maxLength: 20, + pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};:\'",.<>\\/\\\\?]).+$', + }, +}; + const registerBodySchema = { type: 'object', properties: { - nickname: { - type: 'string', - maxLength: 10, - pattern: '^[a-zA-Z0-9가-힣]+$', - }, - email: { - type: 'string', - format: 'email', - maxLength: 50, - }, - password: { - type: 'string', - minLength: 8, - maxLength: 20, - pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};:\'",.<>\\/\\\\?]).+$', - }, + nickname: commonSchemas.nickname, + email: commonSchemas.email, + password: commonSchemas.password, }, required: ['nickname', 'email', 'password'], additionalProperties: false, @@ -25,11 +31,7 @@ const registerBodySchema = { const nicknameCheckReqQuerySchema = { type: 'object', properties: { - nickname: { - type: 'string', - maxLength: 10, - pattern: '^[a-zA-Z0-9가-힣]+$', - }, + nickname: commonSchemas.nickname, }, required: ['nickname'], additionalProperties: false, @@ -38,14 +40,20 @@ const nicknameCheckReqQuerySchema = { const emailCheckReqQuerySchema = { type: 'object', properties: { - email: { - type: 'string', - format: 'email', - maxLength: 50, - }, + email: commonSchemas.email, }, required: ['email'], additionalProperties: false, }; -module.exports = { registerBodySchema, nicknameCheckReqQuerySchema, emailCheckReqQuerySchema }; +const loginBodySchema = { + type: 'object', + properties: { + email: commonSchemas.email, + password: commonSchemas.password, + }, + required: ['email', 'password'], + additionalProperties: false, +}; + +module.exports = { registerBodySchema, nicknameCheckReqQuerySchema, emailCheckReqQuerySchema, loginBodySchema }; From 22410c1b73c4e4d3fb4a11565f5b28ad21040322 Mon Sep 17 00:00:00 2001 From: jjikky Date: Thu, 20 Jun 2024 11:23:33 +0900 Subject: [PATCH 6/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20login=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/constants/error-message.js | 1 + src/routes/user/user.controller.js | 36 ++++++++++++++++----------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/common/constants/error-message.js b/src/common/constants/error-message.js index c37d89b..2376fc7 100644 --- a/src/common/constants/error-message.js +++ b/src/common/constants/error-message.js @@ -21,6 +21,7 @@ const ErrorMessage = Object.freeze({ EMAIL_CHECK_ERROR: '이메일 중복검사중 오류가 발생하였습니다.', EXIST_NICKNAME: '이미 존재하는 닉네임 입니다.', EXIST_EMAIL: '이미 존재하는 이메일 입니다.', + LOGIN_ERROR: '로그인중 오류가 발생하였습니다.', }); module.exports = ErrorMessage; diff --git a/src/routes/user/user.controller.js b/src/routes/user/user.controller.js index 627f1cd..ab4f5ab 100644 --- a/src/routes/user/user.controller.js +++ b/src/routes/user/user.controller.js @@ -110,20 +110,28 @@ exports.isEmailExist = async (req, res) => { // }; exports.localLogin = async (req, res, next) => { - passport.authenticate('local', (authError, user, info) => { - if (authError) { - console.error(authError); - return next(authError); - } - if (!user) { - return sendResponse.unAuthorized(res, { message: info.message }); - } + try { + req.body = validateRequest(loginBodySchema, req.body); + passport.authenticate('local', (authError, user, info) => { + if (authError) { + console.error(authError); + return next(authError); + } + if (!user) { + return sendResponse.unAuthorized(res, { message: info.message }); + } - const token = generateToken(user); + const token = generateToken(user); - return sendResponse.ok(res, { - message: SucesssMessage.LOGIN_SUCCESSS, - token, - }); - })(req, res, next); + return sendResponse.ok(res, { + message: SucesssMessage.LOGIN_SUCCESSS, + token, + }); + })(req, res, next); + } catch (err) { + if (err?.type) { + return sendResponse.badRequest(res, err.message); + } + sendResponse.fail(req, res, ErrorMessage.LOGIN_ERROR); + } }; From 075b7f24975c0b14034efb7b4633c701ba5f3d8c Mon Sep 17 00:00:00 2001 From: jjikky Date: Fri, 21 Jun 2024 02:50:20 +0900 Subject: [PATCH 7/9] =?UTF-8?q?=E2=9E=95=20install=20axios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 86 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 87 insertions(+) diff --git a/package-lock.json b/package-lock.json index 27369ab..b0fca27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "ajv": "^8.16.0", "ajv-formats": "^3.0.1", + "axios": "^1.7.2", "bcrypt": "^5.1.1", "cookie-parser": "^1.4.6", "cors": "^2.8.5", @@ -714,6 +715,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -730,6 +736,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1018,6 +1034,17 @@ "color-support": "bin.js" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", @@ -1260,6 +1287,14 @@ "node": ">= 14" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -2239,6 +2274,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -6093,6 +6141,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -6103,6 +6156,16 @@ "possible-typed-array-names": "^1.0.0" } }, + "axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -6323,6 +6386,14 @@ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", @@ -6502,6 +6573,11 @@ "esprima": "^4.0.1" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -7258,6 +7334,16 @@ "is-callable": "^1.1.3" } }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", diff --git a/package.json b/package.json index 71341a1..5fd3dc5 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "ajv": "^8.16.0", "ajv-formats": "^3.0.1", + "axios": "^1.7.2", "bcrypt": "^5.1.1", "cookie-parser": "^1.4.6", "cors": "^2.8.5", From bf6d14659f1fd12ed24b829f1be47664949ca513 Mon Sep 17 00:00:00 2001 From: jjikky Date: Fri, 21 Jun 2024 02:51:03 +0900 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=94=A7=20cors=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/modules/express/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/modules/express/index.js b/src/common/modules/express/index.js index 665f2f4..c9c4da1 100644 --- a/src/common/modules/express/index.js +++ b/src/common/modules/express/index.js @@ -28,7 +28,7 @@ module.exports = expressLoader = (app) => { cors({ credentials: true, origin: (origin, callback) => { - if (origin !== null || conf.corsWhiteList?.indexOf(origin) !== -1) { + if (origin === undefined || (origin && conf.corsWhiteList?.indexOf(origin) !== -1)) { return callback(null, true); } callback(new Error('CORS ERROR')); From 67009462d0cfce0dba04cd6806086f06f130ae03 Mon Sep 17 00:00:00 2001 From: jjikky Date: Fri, 21 Jun 2024 03:50:38 +0900 Subject: [PATCH 9/9] =?UTF-8?q?=E2=9C=A8=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/config/index.js | 4 ++ src/common/constants/error-message.js | 1 + src/common/constants/success-message.js | 2 + src/common/passport/index.js | 8 ++-- src/common/utils/kakao.js | 37 +++++++++++++++ src/routes/user/user.controller.js | 62 ++++++++++++++----------- src/routes/user/user.model.js | 10 ++-- src/routes/user/user.repository.js | 5 ++ src/routes/user/user.route.js | 8 ++-- src/routes/user/user.service.js | 13 ++++-- 10 files changed, 108 insertions(+), 42 deletions(-) create mode 100644 src/common/utils/kakao.js diff --git a/src/common/config/index.js b/src/common/config/index.js index dcedc7d..27e15d1 100644 --- a/src/common/config/index.js +++ b/src/common/config/index.js @@ -14,6 +14,10 @@ const conf = { mongoURL: process.env.MONGO_URL, jwtSecret: process.env.JWT_SECRET, cookieSecret: process.env.COOKIE_SECRET, + + // social login + kakaoRestApiKey: process.env.KAKAO_REST_API_KEY, + kakaoCallback: process.env.KAKAO_CALLBACK, }; module.exports = conf; diff --git a/src/common/constants/error-message.js b/src/common/constants/error-message.js index 2376fc7..ae79ee2 100644 --- a/src/common/constants/error-message.js +++ b/src/common/constants/error-message.js @@ -22,6 +22,7 @@ const ErrorMessage = Object.freeze({ EXIST_NICKNAME: '이미 존재하는 닉네임 입니다.', EXIST_EMAIL: '이미 존재하는 이메일 입니다.', LOGIN_ERROR: '로그인중 오류가 발생하였습니다.', + KAKAO_LOGIN_ERROR: '카카오 로그인중 오류가 발생하였습니다.', }); module.exports = ErrorMessage; diff --git a/src/common/constants/success-message.js b/src/common/constants/success-message.js index 944f7a6..f435bb8 100644 --- a/src/common/constants/success-message.js +++ b/src/common/constants/success-message.js @@ -6,6 +6,8 @@ const SucesssMessage = Object.freeze({ // USER - 로그인 LOGIN_SUCCESSS: '로그인 성공', + + GET_PROFILE_SUCCESS: '유저 정보 조회 성공', }); module.exports = SucesssMessage; diff --git a/src/common/passport/index.js b/src/common/passport/index.js index 884a1f5..8fc0bdf 100644 --- a/src/common/passport/index.js +++ b/src/common/passport/index.js @@ -1,6 +1,7 @@ const passport = require('passport'); -const local = require('./localStrategy'); +const localStrategy = require('./localStrategy'); const jwtStrategy = require('./jwtStrategy'); +const User = require('../../routes/user/user.model'); module.exports = () => { passport.serializeUser((user, done) => { @@ -16,6 +17,7 @@ module.exports = () => { } }); - local(); // LocalStrategy 초기화 - jwtStrategy(); // JWTStrategy 초기화 + // 초기화 + localStrategy(); + jwtStrategy(); }; diff --git a/src/common/utils/kakao.js b/src/common/utils/kakao.js new file mode 100644 index 0000000..00db064 --- /dev/null +++ b/src/common/utils/kakao.js @@ -0,0 +1,37 @@ +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, + }; + + 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 }; +}; +exports.getUserInfo = async (accessToken) => { + // 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; + + 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, + }; +}; diff --git a/src/routes/user/user.controller.js b/src/routes/user/user.controller.js index ab4f5ab..c5f7f52 100644 --- a/src/routes/user/user.controller.js +++ b/src/routes/user/user.controller.js @@ -1,6 +1,4 @@ const passport = require('passport'); -const jwt = require('jsonwebtoken'); - const userService = require('./user.service'); const sendResponse = require('../../common/utils/response-handler'); const ErrorMessage = require('../../common/constants/error-message'); @@ -12,8 +10,8 @@ const { emailCheckReqQuerySchema, loginBodySchema, } = require('./user.schema'); -const config = require('../../common/config'); const { generateToken } = require('../../common/utils/auth'); +const { getKakaoToken, getUserInfo } = require('../../common/utils/kakao'); exports.register = async (req, res) => { try { @@ -85,30 +83,6 @@ exports.isEmailExist = async (req, res) => { } }; -// const { email, password } = validateRequest(loginBodySchema, req.body); -// exports.localLogin = async (req, res) => { - -// passport.authenticate('local', (authError, user, info) => { -// if (authError) { -// console.error(authError); -// return next(authError); -// } -// if (!user) { -// return sendResponse.unAuthorized(res, { message: info.message }); -// } -// return req.login(user, (loginError) => { -// if (loginError) { -// console.error(loginError); -// return next(loginError); -// } -// return sendResponse.ok(res, { -// message: SucesssMessage.LOGIN_SUCCESSS, -// user_id: user._id, -// }); -// }); -// })(req, res, next); -// }; - exports.localLogin = async (req, res, next) => { try { req.body = validateRequest(loginBodySchema, req.body); @@ -120,7 +94,6 @@ exports.localLogin = async (req, res, next) => { if (!user) { return sendResponse.unAuthorized(res, { message: info.message }); } - const token = generateToken(user); return sendResponse.ok(res, { @@ -135,3 +108,36 @@ exports.localLogin = async (req, res, next) => { sendResponse.fail(req, res, ErrorMessage.LOGIN_ERROR); } }; + +exports.kakaoLogin = async (req, res) => { + try { + const { code } = req.body; + + const { accessToken } = await getKakaoToken(code); + const { snsId, email, nickname } = await getUserInfo(accessToken); + + const kakaoUser = { snsId: snsId, email, nickname, provider: 'kakao' }; + + let user = await userService.isKaKaoUserExist(kakaoUser.snsId); + if (!user) { + user = await userService.kakaoRegister(kakaoUser); + } + const token = generateToken(user); + sendResponse.ok(res, { + message: SucesssMessage.LOGIN_SUCCESSS, + token, + }); + } catch (err) { + console.log(err); + sendResponse.fail(req, res, ErrorMessage.KAKAO_LOGIN_ERROR); + } +}; + +exports.getProfile = (req, res) => { + const { _id, nickname, email } = req.user; + const data = { _id, nickname, email }; + sendResponse.ok(res, { + message: SucesssMessage.GET_PROFILE_SUCCESS, + data, + }); +}; diff --git a/src/routes/user/user.model.js b/src/routes/user/user.model.js index b10b810..4fae302 100644 --- a/src/routes/user/user.model.js +++ b/src/routes/user/user.model.js @@ -23,9 +23,13 @@ const userSchema = new mongoose.Schema( ); userSchema.pre('save', async function (next) { - if (this.isModified('password') || this.isNew) { - const salt = await bcrypt.genSalt(10); - this.password = await bcrypt.hash(this.password, salt); + try { + if ((this.isNew && !this.provider) || this.isModified('password')) { + const salt = await bcrypt.genSalt(10); + this.password = await bcrypt.hash(this.password, salt); + } + } catch (err) { + console.log(err); } next(); }); diff --git a/src/routes/user/user.repository.js b/src/routes/user/user.repository.js index f2236a5..1b6e995 100644 --- a/src/routes/user/user.repository.js +++ b/src/routes/user/user.repository.js @@ -18,3 +18,8 @@ exports.findUserByEmail = async (email) => { const userExists = await User.exists({ email }); return userExists ? true : false; }; + +exports.getUserBySnsId = async (snsId) => { + const user = await User.findOne({ snsId, provider: 'kakao' }); + return user; +}; diff --git a/src/routes/user/user.route.js b/src/routes/user/user.route.js index bbb9a2e..e20b27d 100644 --- a/src/routes/user/user.route.js +++ b/src/routes/user/user.route.js @@ -1,5 +1,5 @@ const express = require('express'); -const { register, isNicknameExist, isEmailExist, localLogin } = require('./user.controller'); +const { register, isNicknameExist, isEmailExist, localLogin, kakaoLogin, getProfile } = require('./user.controller'); const { isLoggedIn, isNotLoggedIn } = require('../../common/utils/auth'); const userRouter = express.Router(); @@ -10,10 +10,8 @@ userRouter.get('/check/email', isEmailExist); // 로그인 userRouter.post('/local/login', isNotLoggedIn, localLogin); +userRouter.post('/kakao/login', isNotLoggedIn, kakaoLogin); -// NOTE : JWT 확인용. 삭제 예정 -userRouter.get('/profile', isLoggedIn, (req, res) => { - res.json({ user: req.user }); -}); +userRouter.get('/profile', isLoggedIn, getProfile); module.exports = userRouter; diff --git a/src/routes/user/user.service.js b/src/routes/user/user.service.js index 25d2e25..4c6db44 100644 --- a/src/routes/user/user.service.js +++ b/src/routes/user/user.service.js @@ -1,16 +1,18 @@ -const bcrypt = require('bcrypt'); const userRepository = require('./user.repository'); exports.register = async (userData) => { - const hash = await bcrypt.hash(userData.password, 12); const newUser = { email: userData.email, - password: hash, + password: userData.password, nickname: userData.nickname, }; return await userRepository.createUser(newUser); }; +exports.kakaoRegister = async (newUser) => { + return await userRepository.createUser(newUser); +}; + exports.isNicknameExist = async (nickname) => { const isUserExist = await userRepository.findUserByNickname(nickname); return isUserExist; @@ -20,3 +22,8 @@ exports.isEmailExist = async (email) => { const isUserExist = await userRepository.findUserByEmail(email); return isUserExist; }; + +exports.isKaKaoUserExist = async (snsId) => { + const user = await userRepository.getUserBySnsId(snsId); + return user; +};