Showing
6 changed files
with
213 additions
and
18 deletions
| ... | @@ -12,6 +12,7 @@ | ... | @@ -12,6 +12,7 @@ |
| 12 | "@google-cloud/storage": "^5.14.2", | 12 | "@google-cloud/storage": "^5.14.2", |
| 13 | "@koa/cors": "^3.1.0", | 13 | "@koa/cors": "^3.1.0", |
| 14 | "firebase-admin": "^9.11.1", | 14 | "firebase-admin": "^9.11.1", |
| 15 | + "google-auth-library": "^7.10.0", | ||
| 15 | "koa-body": "^4.2.0", | 16 | "koa-body": "^4.2.0", |
| 16 | "moment": "^2.29.1", | 17 | "moment": "^2.29.1", |
| 17 | "moment-timezone": "^0.5.33", | 18 | "moment-timezone": "^0.5.33", |
| ... | @@ -1741,9 +1742,9 @@ | ... | @@ -1741,9 +1742,9 @@ |
| 1741 | } | 1742 | } |
| 1742 | }, | 1743 | }, |
| 1743 | "node_modules/google-auth-library": { | 1744 | "node_modules/google-auth-library": { |
| 1744 | - "version": "7.9.2", | 1745 | + "version": "7.10.0", |
| 1745 | - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.9.2.tgz", | 1746 | + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.10.0.tgz", |
| 1746 | - "integrity": "sha512-HjxbJt660a+YUTYAgYor87JCuBZvjUSNBExk4bXTEaMuCn8IHSDeHmFxKqThuDPrLCiKJp8blk/Ze8f7SI4N6g==", | 1747 | + "integrity": "sha512-ICsqaU+lxMHVlDUzMrfVIEqnARw2AwBiZ/2KnNM6BcTf9Nott+Af87DTIzmlnW865p3REUP2MVL0xkPC3a61aQ==", |
| 1747 | "dependencies": { | 1748 | "dependencies": { |
| 1748 | "arrify": "^2.0.0", | 1749 | "arrify": "^2.0.0", |
| 1749 | "base64-js": "^1.3.0", | 1750 | "base64-js": "^1.3.0", |
| ... | @@ -5182,9 +5183,9 @@ | ... | @@ -5182,9 +5183,9 @@ |
| 5182 | } | 5183 | } |
| 5183 | }, | 5184 | }, |
| 5184 | "google-auth-library": { | 5185 | "google-auth-library": { |
| 5185 | - "version": "7.9.2", | 5186 | + "version": "7.10.0", |
| 5186 | - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.9.2.tgz", | 5187 | + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.10.0.tgz", |
| 5187 | - "integrity": "sha512-HjxbJt660a+YUTYAgYor87JCuBZvjUSNBExk4bXTEaMuCn8IHSDeHmFxKqThuDPrLCiKJp8blk/Ze8f7SI4N6g==", | 5188 | + "integrity": "sha512-ICsqaU+lxMHVlDUzMrfVIEqnARw2AwBiZ/2KnNM6BcTf9Nott+Af87DTIzmlnW865p3REUP2MVL0xkPC3a61aQ==", |
| 5188 | "requires": { | 5189 | "requires": { |
| 5189 | "arrify": "^2.0.0", | 5190 | "arrify": "^2.0.0", |
| 5190 | "base64-js": "^1.3.0", | 5191 | "base64-js": "^1.3.0", | ... | ... |
| ... | @@ -20,6 +20,7 @@ | ... | @@ -20,6 +20,7 @@ |
| 20 | "@google-cloud/storage": "^5.14.2", | 20 | "@google-cloud/storage": "^5.14.2", |
| 21 | "@koa/cors": "^3.1.0", | 21 | "@koa/cors": "^3.1.0", |
| 22 | "firebase-admin": "^9.11.1", | 22 | "firebase-admin": "^9.11.1", |
| 23 | + "google-auth-library": "^7.10.0", | ||
| 23 | "koa-body": "^4.2.0", | 24 | "koa-body": "^4.2.0", |
| 24 | "moment": "^2.29.1", | 25 | "moment": "^2.29.1", |
| 25 | "moment-timezone": "^0.5.33", | 26 | "moment-timezone": "^0.5.33", | ... | ... |
| ... | @@ -93,6 +93,7 @@ exports.searchHospital = async ctx => { | ... | @@ -93,6 +93,7 @@ exports.searchHospital = async ctx => { |
| 93 | }; | 93 | }; |
| 94 | }; | 94 | }; |
| 95 | 95 | ||
| 96 | +//의사 회원가입 | ||
| 96 | exports.doctorRegister = async ctx => { | 97 | exports.doctorRegister = async ctx => { |
| 97 | const { | 98 | const { |
| 98 | userId, | 99 | userId, |
| ... | @@ -193,6 +194,7 @@ exports.doctorRegister = async ctx => { | ... | @@ -193,6 +194,7 @@ exports.doctorRegister = async ctx => { |
| 193 | 194 | ||
| 194 | } | 195 | } |
| 195 | 196 | ||
| 197 | +//로컬 로그인 | ||
| 196 | exports.login = async(ctx) => { | 198 | exports.login = async(ctx) => { |
| 197 | const { userId, password, deviceToken } = ctx.request.body; | 199 | const { userId, password, deviceToken } = ctx.request.body; |
| 198 | 200 | ||
| ... | @@ -211,7 +213,7 @@ exports.login = async(ctx) => { | ... | @@ -211,7 +213,7 @@ exports.login = async(ctx) => { |
| 211 | } | 213 | } |
| 212 | 214 | ||
| 213 | const user = await User.findByUserId(userId); | 215 | const user = await User.findByUserId(userId); |
| 214 | - if(!user || !user.userTypeCd) { | 216 | + if(!user || !user.userTypeCd || user.authTypeCd !== 'NORMAL') { |
| 215 | ctx.status = 401; | 217 | ctx.status = 401; |
| 216 | ctx.body = { | 218 | ctx.body = { |
| 217 | error : '존재하지 않는 회원입니다.', | 219 | error : '존재하지 않는 회원입니다.', |
| ... | @@ -259,6 +261,180 @@ exports.login = async(ctx) => { | ... | @@ -259,6 +261,180 @@ exports.login = async(ctx) => { |
| 259 | 261 | ||
| 260 | }; | 262 | }; |
| 261 | 263 | ||
| 264 | +//social Register | ||
| 265 | +exports.socialRegister = async ctx => { | ||
| 266 | + const { socialType } = ctx.params; | ||
| 267 | + const { accessToken, deviceToken } = ctx.request.body; | ||
| 268 | + | ||
| 269 | + const verifyingToken = | ||
| 270 | + socialType.toUpperCase() === 'GOOGLE' ? async () => { | ||
| 271 | + //id_token | ||
| 272 | + const result = jwt.decode(accessToken); | ||
| 273 | + | ||
| 274 | + return { | ||
| 275 | + userId : result.email, | ||
| 276 | + userNm : result.name, | ||
| 277 | + contact : null, | ||
| 278 | + birth : null, | ||
| 279 | + }; | ||
| 280 | + } | ||
| 281 | + : socialType.toUpperCase() === 'NAVER' ? async () => { | ||
| 282 | + const url = 'https://openapi.naver.com/v1/nid/me'; | ||
| 283 | + const result = await axios.get(url, { | ||
| 284 | + headers : { | ||
| 285 | + Authorization : `Bearer ${accessToken}`, | ||
| 286 | + }, | ||
| 287 | + }); | ||
| 288 | + | ||
| 289 | + const { email, mobile, name, birthday, birthyear } = result.data.response; | ||
| 290 | + | ||
| 291 | + return { | ||
| 292 | + userId : email, | ||
| 293 | + userNm : name, | ||
| 294 | + contact : mobile, | ||
| 295 | + birth : `${birthyear}-${birthday}`, | ||
| 296 | + }; | ||
| 297 | + } | ||
| 298 | + : socialType.toUpperCase() === 'KAKAO' ? async () => { | ||
| 299 | + const url = 'https://kapi.kakao.com/v2/user/me'; | ||
| 300 | + const result = await axios.get(url, { | ||
| 301 | + headers : { | ||
| 302 | + Authorization : `Bearer ${accessToken}`, | ||
| 303 | + }, | ||
| 304 | + }); | ||
| 305 | + | ||
| 306 | + console.log(result); | ||
| 307 | + | ||
| 308 | + return result; | ||
| 309 | + } : () => null; | ||
| 310 | + | ||
| 311 | + | ||
| 312 | + const verifyingInfo = await verifyingToken(); | ||
| 313 | + if(!verifyingInfo || !verifyingInfo.userId) { | ||
| 314 | + ctx.status = 403; | ||
| 315 | + ctx.body = { | ||
| 316 | + error : '잘못된 요청', | ||
| 317 | + }; | ||
| 318 | + | ||
| 319 | + return; | ||
| 320 | + } | ||
| 321 | + | ||
| 322 | + const { userId, userNm, birth, contact } = verifyingInfo; | ||
| 323 | + | ||
| 324 | + const existUser = await User.findByUserId(userId); | ||
| 325 | + if(existUser) { | ||
| 326 | + ctx.status = 409; | ||
| 327 | + ctx.body = { | ||
| 328 | + error : '이미 가입된 회원', | ||
| 329 | + }; | ||
| 330 | + | ||
| 331 | + return; | ||
| 332 | + } | ||
| 333 | + | ||
| 334 | + const user = new User({ | ||
| 335 | + userId, | ||
| 336 | + hashedPassword : null, | ||
| 337 | + authTypeCd : socialType.toUpperCase(), | ||
| 338 | + useYn : 'Y', | ||
| 339 | + }); | ||
| 340 | + | ||
| 341 | + const profile = new Profile({ | ||
| 342 | + userId, | ||
| 343 | + userNm, | ||
| 344 | + birth, | ||
| 345 | + contact, | ||
| 346 | + deviceToken, | ||
| 347 | + }); | ||
| 348 | + | ||
| 349 | + await user.save(); | ||
| 350 | + await profile.save(); | ||
| 351 | + | ||
| 352 | + ctx.status = 201; | ||
| 353 | + | ||
| 354 | +}; | ||
| 355 | + | ||
| 356 | +//social Login | ||
| 357 | +exports.socialLogin = async ctx => { | ||
| 358 | + const { socialType } = ctx.params; | ||
| 359 | + const { accessToken, deviceToken, } = ctx.request.body; | ||
| 360 | + | ||
| 361 | + const verifyingToken = | ||
| 362 | + socialType.toUpperCase() === 'GOOGLE' ? async () => { | ||
| 363 | + //id_token : google Login | ||
| 364 | + const result = jwt.decode(accessToken); | ||
| 365 | + | ||
| 366 | + return result.email; | ||
| 367 | + } | ||
| 368 | + : socialType.toUpperCase() === 'NAVER' ? async () => { | ||
| 369 | + //naver Login | ||
| 370 | + const url = 'https://openapi.naver.com/v1/nid/me'; | ||
| 371 | + const result = await axios.get(url, { | ||
| 372 | + headers : { | ||
| 373 | + Authorization : `Bearer ${accessToken}`, | ||
| 374 | + }, | ||
| 375 | + }); | ||
| 376 | + | ||
| 377 | + return result.data.response.email; | ||
| 378 | + } | ||
| 379 | + : socialType.toUpperCase() === 'KAKAO' ? async () => { | ||
| 380 | + //kakao Login | ||
| 381 | + const url = 'https://kapi.kakao.com/v2/user/me'; | ||
| 382 | + const result = await axios.get(url, { | ||
| 383 | + headers : { | ||
| 384 | + Authorization : `Bearer ${accessToken}`, | ||
| 385 | + }, | ||
| 386 | + }); | ||
| 387 | + | ||
| 388 | + console.log(result); | ||
| 389 | + | ||
| 390 | + return result; | ||
| 391 | + } : () => null; | ||
| 392 | + | ||
| 393 | + | ||
| 394 | + const userId = await verifyingToken(); | ||
| 395 | + if(!userId) { | ||
| 396 | + ctx.status = 403; | ||
| 397 | + ctx.body = { | ||
| 398 | + error : '잘못된 요청입니다', | ||
| 399 | + }; | ||
| 400 | + | ||
| 401 | + return; | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + const user = await User.findByUserId(userId); | ||
| 405 | + if(!user || user.useYn !== 'Y') { | ||
| 406 | + ctx.status = 404; | ||
| 407 | + ctx.body = { | ||
| 408 | + error : '존재하지 않는 회원입니다.', | ||
| 409 | + }; | ||
| 410 | + | ||
| 411 | + return; | ||
| 412 | + } else if (user.authTypeCd !== socialType.toUpperCase()) { | ||
| 413 | + ctx.status = 400; | ||
| 414 | + ctx.body = { | ||
| 415 | + error : '잘못된 소셜 로그인입니다.', | ||
| 416 | + }; | ||
| 417 | + | ||
| 418 | + return; | ||
| 419 | + } | ||
| 420 | + | ||
| 421 | + const profile = await Profile.findOne({ userId }); | ||
| 422 | + if(profile.deviceToken !== deviceToken) { | ||
| 423 | + profile.updateDeviceToken(deviceToken); | ||
| 424 | + await profile.save(); | ||
| 425 | + } | ||
| 426 | + | ||
| 427 | + | ||
| 428 | + const token = await user.generateToken(); | ||
| 429 | + | ||
| 430 | + ctx.status = 200; | ||
| 431 | + ctx.body = { | ||
| 432 | + userTypeCd : user.userTypeCd, | ||
| 433 | + token, | ||
| 434 | + }; | ||
| 435 | + | ||
| 436 | +}; | ||
| 437 | + | ||
| 262 | exports.logout = async(ctx) => { | 438 | exports.logout = async(ctx) => { |
| 263 | ctx.cookies.set('access_token', null, { | 439 | ctx.cookies.set('access_token', null, { |
| 264 | httpOnly : true, | 440 | httpOnly : true, | ... | ... |
| ... | @@ -5,7 +5,7 @@ const authCtrl = require('./auth.ctrl') | ... | @@ -5,7 +5,7 @@ const authCtrl = require('./auth.ctrl') |
| 5 | const auth = new Router() | 5 | const auth = new Router() |
| 6 | 6 | ||
| 7 | /** | 7 | /** |
| 8 | - * 회원가입 (email type) : 환자 회원가입 | 8 | + * 로컬 회원가입 (email type) : 환자 회원가입 |
| 9 | * url : http://localhost:4000/api/auth/register | 9 | * url : http://localhost:4000/api/auth/register |
| 10 | * request parameter : userId, password, passwordCheck | 10 | * request parameter : userId, password, passwordCheck |
| 11 | * return : null | 11 | * return : null |
| ... | @@ -21,7 +21,7 @@ auth.post('/register', authCtrl.register) | ... | @@ -21,7 +21,7 @@ auth.post('/register', authCtrl.register) |
| 21 | auth.get('/hospital', authCtrl.searchHospital); | 21 | auth.get('/hospital', authCtrl.searchHospital); |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | - * 회원가입 (email type) : 의사 회원가입 | 24 | + * 로컬 회원가입 (email type) : 의사 회원가입 |
| 25 | * url : http://localhost:4000/api/auth/register/doctor | 25 | * url : http://localhost:4000/api/auth/register/doctor |
| 26 | * request parameter : userId, password, passwordCheck, doctorInfo(File) | 26 | * request parameter : userId, password, passwordCheck, doctorInfo(File) |
| 27 | * return : null | 27 | * return : null |
| ... | @@ -29,14 +29,30 @@ auth.get('/hospital', authCtrl.searchHospital); | ... | @@ -29,14 +29,30 @@ auth.get('/hospital', authCtrl.searchHospital); |
| 29 | auth.post('/register/doctor', KoaBody, authCtrl.doctorRegister) | 29 | auth.post('/register/doctor', KoaBody, authCtrl.doctorRegister) |
| 30 | 30 | ||
| 31 | /** | 31 | /** |
| 32 | - * 로그인 (email type) | 32 | + * 로컬 로그인 (email type) |
| 33 | * url : http://localhost:4000/api/auth/login | 33 | * url : http://localhost:4000/api/auth/login |
| 34 | * request parameter : userId, password | 34 | * request parameter : userId, password |
| 35 | - * return : userId | 35 | + * return : token, userTypeCd |
| 36 | */ | 36 | */ |
| 37 | auth.post('/login', authCtrl.login) | 37 | auth.post('/login', authCtrl.login) |
| 38 | 38 | ||
| 39 | /** | 39 | /** |
| 40 | + * 소셜 회원가입(Google, Naver, Kakao) | ||
| 41 | + * url : http://localhost:4000/api/auth/register/${socialType} | ||
| 42 | + * request parameter : accessToken | ||
| 43 | + * return : status | ||
| 44 | + */ | ||
| 45 | +auth.post('/register/social/:socialType', authCtrl.socialRegister); | ||
| 46 | + | ||
| 47 | +/** | ||
| 48 | + * 소셜 로그인(Google, Naver, Kakao) | ||
| 49 | + * url : http://localhost:4000/api/auth/login/${socialType} | ||
| 50 | + * request parameter | ||
| 51 | + * return : token, userTypeCd | ||
| 52 | + */ | ||
| 53 | +auth.post('/login/social/:socialType', authCtrl.socialLogin); | ||
| 54 | + | ||
| 55 | +/** | ||
| 40 | * 로그아웃 | 56 | * 로그아웃 |
| 41 | * url : http://localhost:4000/api/auth/logout | 57 | * url : http://localhost:4000/api/auth/logout |
| 42 | * request parameter : null | 58 | * request parameter : null | ... | ... |
| ... | @@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); | ... | @@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); |
| 5 | const Schema = mongoose.Schema; | 5 | const Schema = mongoose.Schema; |
| 6 | 6 | ||
| 7 | const UserSchema = new Schema ({ | 7 | const UserSchema = new Schema ({ |
| 8 | - userId : { type: String, required : true, unique : true, lowercase : true, }, | 8 | + userId : { type: String, required : true, unique : true, lowercase : true, trim : true }, |
| 9 | - hashedPassword : { type : String, required : true }, | 9 | + hashedPassword : { type : String, required : true, }, |
| 10 | - userTypeCd : { type : String, required : true, default : 'NORMAL' }, | 10 | + userTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, }, |
| 11 | + authTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, }, | ||
| 11 | useYn : { type : String, default : 'W', required : true, }, | 12 | useYn : { type : String, default : 'W', required : true, }, |
| 12 | }); | 13 | }); |
| 13 | 14 | ... | ... |
| ... | @@ -1134,10 +1134,10 @@ | ... | @@ -1134,10 +1134,10 @@ |
| 1134 | dependencies: | 1134 | dependencies: |
| 1135 | "type-fest" "^0.20.2" | 1135 | "type-fest" "^0.20.2" |
| 1136 | 1136 | ||
| 1137 | -"google-auth-library@^7.0.0", "google-auth-library@^7.0.2", "google-auth-library@^7.6.1": | 1137 | +"google-auth-library@^7.0.0", "google-auth-library@^7.0.2", "google-auth-library@^7.10.0", "google-auth-library@^7.6.1": |
| 1138 | - "integrity" "sha512-HjxbJt660a+YUTYAgYor87JCuBZvjUSNBExk4bXTEaMuCn8IHSDeHmFxKqThuDPrLCiKJp8blk/Ze8f7SI4N6g==" | 1138 | + "integrity" "sha512-ICsqaU+lxMHVlDUzMrfVIEqnARw2AwBiZ/2KnNM6BcTf9Nott+Af87DTIzmlnW865p3REUP2MVL0xkPC3a61aQ==" |
| 1139 | - "resolved" "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.9.2.tgz" | 1139 | + "resolved" "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.10.0.tgz" |
| 1140 | - "version" "7.9.2" | 1140 | + "version" "7.10.0" |
| 1141 | dependencies: | 1141 | dependencies: |
| 1142 | "arrify" "^2.0.0" | 1142 | "arrify" "^2.0.0" |
| 1143 | "base64-js" "^1.3.0" | 1143 | "base64-js" "^1.3.0" | ... | ... |
-
Please register or login to post a comment