Skip to content

Commit

Permalink
Merge pull request #9 from TeamSparker/feature/#7
Browse files Browse the repository at this point in the history
[FEAT] checkUser 미들웨어 구현
  • Loading branch information
xxeol2 authored Jan 11, 2022
2 parents 5f3b517 + 688c025 commit c6a1f0e
Show file tree
Hide file tree
Showing 12 changed files with 1,690 additions and 3,490 deletions.
63 changes: 63 additions & 0 deletions functions/api/routes/auth/authSignUpPOST.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const util = require('../../../lib/util');
const statusCode = require('../../../constants/statusCode');
const responseMessage = require('../../../constants/responseMessage');
const db = require('../../../db/db');
const { userDB } = require('../../../db');
const jwtHandlers = require('../../../lib/jwtHandlers');

/**
* @회원가입
* @route POST /auth/signup
* @body socialId:string, nickname:string, profileImg:file
* @error
* 1. socialId/nickname이 전달되지 않음
* 2. 이미 존재하는 socialId
* 3. 닉네임 10자 초과
*/

module.exports = async (req, res) => {
const { socialId, nickname } = req.body;
const profileImg = req.imageUrls;

console.log(socialId, nickname, profileImg);

// @error 1. socialId/nickname이 전달되지 않음
if (!socialId || !nickname) {
return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.NULL_VALUE));
}

let client;

try {
client = await db.connect();

// @error 2. 이미 존재하는 socialIds
const alreaySocialId = await userDB.getUserBySocialId(client, socialId);
if(alreaySocialId) {
return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.ALREADY_SOCIALID));
}

let user;

if (profileImg.length) {
user = await userDB.addUser(client, socialId, nickname, profileImg[0]);
} else {
user = await userDB.addUser(client, socialId, nickname, null);
}

const { accesstoken } = jwtHandlers.sign(user);

console.log(user);

res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.CREATED_USER, { user, accesstoken }));
} catch (error) {
console.log(error);
functions.logger.error(`[SIGNUP ERROR] [${req.method.toUpperCase()}] ${req.originalUrl}`, `[CONTENT] email:${socialId} ${error}`);

res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, responseMessage.INTERNAL_SERVER_ERROR));
} finally {
client.release();
}
};
30 changes: 30 additions & 0 deletions functions/api/routes/auth/authTestGET.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const util = require('../../../lib/util');
const statusCode = require('../../../constants/statusCode');
const responseMessage = require('../../../constants/responseMessage');
const db = require('../../../db/db');
const { userDB } = require('../../../db');
const jwtHandlers = require('../../../lib/jwtHandlers');


module.exports = async (req, res) => {
const user = req.user;
console.log(user);
if (!user) return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.NO_USER));

let client;

try {
client = await db.connect();

res.status(statusCode.OK).send(util.success(statusCode.OK, "token -> user 활용법", user));
} catch (error) {
console.log(error);
functions.logger.error(`[ERROR] [${req.method.toUpperCase()}] ${req.originalUrl}`, `[CONTENT] ${error}`);

res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, responseMessage.INTERNAL_SERVER_ERROR));
} finally {
client.release();
}
};
6 changes: 5 additions & 1 deletion functions/api/routes/auth/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const express = require('express');
const router = express.Router();
const uploadImage = require('../../../middlewares/uploadImage');
const { checkUser } = require('../../../middlewares/auth');

// router.post('/signup', require('./userSignupPOST'));
router.post('/signup',uploadImage, require('./authSignupPOST'));
// router.get('/test', checkUser, require('./authTestGET'));
router.get('/test', checkUser, require('./authTestGET'));

module.exports = router;
7 changes: 7 additions & 0 deletions functions/constants/jwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const TOKEN_EXPIRED = -3;
const TOKEN_INVALID = -2;

module.exports = {
TOKEN_EXPIRED,
TOKEN_INVALID,
};
2 changes: 1 addition & 1 deletion functions/constants/responseMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = {
// 회원가입
CREATED_USER: '회원 가입 성공',
DELETE_USER: '회원 탈퇴 성공',
ALREADY_EMAIL: '이미 사용중인 이메일입니다.',
ALREADY_SOCIALID: '이미 사용중인 소셜 아이디입니다.',

// 로그인
LOGIN_SUCCESS: '로그인 성공',
Expand Down
2 changes: 1 addition & 1 deletion functions/db/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,4 @@ const connect = async (req) => {

module.exports = {
connect,
};
};
94 changes: 14 additions & 80 deletions functions/db/user.js
Original file line number Diff line number Diff line change
@@ -1,108 +1,42 @@
const _ = require('lodash');
const convertSnakeToCamel = require('../lib/convertSnakeToCamel');

const getAllUsers = async (client) => {
const { rows } = await client.query(
`
SELECT * FROM "user" u
WHERE is_deleted = FALSE
`,
);
return convertSnakeToCamel.keysToCamel(rows);
};

const getUserById = async (client, userId) => {
const { rows } = await client.query(
`
SELECT * FROM "user" u
WHERE id = $1
SELECT * FROM spark.user u
WHERE user_id = $1
AND is_deleted = FALSE
`,
// client.query()의 두 번째 파라미터에는, 쿼리문에 집어넣고 싶은 변수들의 배열을 적습니다.
// $1에는 배열의 첫번째 변수가, $2에는 배열의 두 번째 변수... 이런 식으로 쿼리문에 변수가 들어가게 됩니다!
[userId],
);
// 위의 getAllUsers와는 달리, 이번에는 유저 하나만 가져오고 싶기 때문에 rows[0]만 리턴해 줍니다.
return convertSnakeToCamel.keysToCamel(rows[0]);
};
}

const getUserByIdFirebase = async (client, idFirebase) => {
const getUserBySocialId = async (client, socialId) => {
const { rows } = await client.query(
`
SELECT * FROM "user" u
WHERE id_firebase = $1
SELECT * FROM spark.user u
WHERE social_id = $1
AND is_deleted = FALSE
`,
[idFirebase],
[socialId],
);
return convertSnakeToCamel.keysToCamel(rows[0]);
};

const getUserByEmail = async (client, email) => {
const addUser = async (client, socialId, nickname, profileImg) => {
const { rows } = await client.query(
`
SELECT * FROM "user" u
WHERE email = $1
AND is_deleted = FALSE
`,
[email],
);
return convertSnakeToCamel.keysToCamel(rows[0]);
};

const updateUser = async (client, username, phone, userId) => {
const { rows: existingRows } = await client.query(
`
SELECT * FROM "user"
WHERE id = $1
AND is_deleted = FALSE
`,
[userId],
);

if (existingRows.length === 0) return false;

const data = _.merge({}, convertSnakeToCamel.keysToCamel(existingRows[0]), { username, phone });

const { rows } = await client.query(
`
UPDATE "user" u
SET username = $1, phone = $2, updated_at = now()
WHERE id = $3
RETURNING *
`,
[data.username, data.phone, userId],
);
return convertSnakeToCamel.keysToCamel(rows[0]);
};

const deleteUser = async (client, userId) => {
const { rows } = await client.query(
`
UPDATE "user" u
SET is_deleted = TRUE, updated_at = now()
WHERE id = $1
RETURNING *
`,
[userId],
);

return convertSnakeToCamel.keysToCamel(rows[0]);
};

const addUser = async (client, email, username, phone, idFirebase) => {
const { rows } = await client.query(
`
INSERT INTO "user"
(email, username, phone, id_firebase)
INSERT INTO spark.user
(social_id, nickname, profile_img)
VALUES
($1, $2, $3, $4)
RETURNING *
($1, $2, $3)
RETURNING user_id, nickname, profile_img
`,

[email, username, phone, idFirebase],
[socialId, nickname, profileImg],
);
return convertSnakeToCamel.keysToCamel(rows[0]);
};

module.exports = { getAllUsers, getUserById, getUserByIdFirebase, getUserByEmail, updateUser, deleteUser, addUser };
module.exports = { getUserById, getUserBySocialId, addUser };
21 changes: 7 additions & 14 deletions functions/lib/jwtHandlers.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,48 @@
const functions = require('firebase-functions');
const jwt = require('jsonwebtoken');
const { TOKEN_INVALID, TOKEN_EXPIRED } = require('../constants/jwt');

// JWT를 발급/인증할 떄 필요한 secretKey를 설정합니다. 값은 .env로부터 불러옵니다.
const secretKey = process.env.JWT_SECRET;
const options = {
algorithm: 'HS256',
expiresIn: '30d',
issuer: 'wesopt',
};

// id, email, name, idFirebase가 담긴 JWT를 발급합니다.
const sign = (user) => {
const payload = {
id: user.id,
email: user.email,
name: user.name || null,
idFirebase: user.idFirebase,
const payload = {
userId: user.userId
};

const result = {
accesstoken: jwt.sign(payload, secretKey, options),
// refreshToken: jwt.sign(payload, secretKey, refreshOptions),
};
return result;
};

// JWT를 해독하고, 해독한 JWT가 우리가 만든 JWT가 맞는지 확인합니다 (인증).
const verify = (token) => {
let decoded;
try {
// console.log("token:",token);
decoded = jwt.verify(token, secretKey);
} catch (err) {
if (err.message === 'jwt expired') {
console.log('expired token');
functions.logger.error('expired token');
return TOKEN_EXPIRED;
} else if (err.message === 'invalid token') {
console.log("decoded:", decoded);
console.log('invalid token');
functions.logger.error('invalid token');
console.log(TOKEN_INVALID);
return TOKEN_INVALID;
} else {
console.log('invalid token');
functions.logger.error('invalid token');
return TOKEN_INVALID;
}
}
// 해독 / 인증이 완료되면, 해독된 상태의 JWT를 반환합니다.
return decoded;
};

module.exports = {
sign,
verify,
};
};
46 changes: 46 additions & 0 deletions functions/middlewares/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const functions = require('firebase-functions');
const jwtHandlers = require('../lib/jwtHandlers');
const db = require('../db/db');
const util = require('../lib/util');
const statusCode = require('../constants/statusCode');
const responseMessage = require('../constants/responseMessage');
const { userDB } = require('../db');
const { TOKEN_INVALID, TOKEN_EXPIRED } = require('../constants/jwt');

const checkUser = async (req, res, next) => {

let client;
try {
client = await db.connect(req);

let user;

if (!req.headers) return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.NO_AUTH_HEADER));

const token = String(req.headers.authorization || '');
if (!token) return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.TOKEN_EMPTY));
const decodedToken = jwtHandlers.verify(token);
if (decodedToken === TOKEN_EXPIRED) return res.status(statusCode.UNAUTHORIZED).send(util.fail(statusCode.UNAUTHORIZED, responseMessage.TOKEN_EXPIRED));
if (decodedToken === TOKEN_INVALID) return res.status(statusCode.UNAUTHORIZED).send(util.fail(statusCode.UNAUTHORIZED, responseMessage.TOKEN_INVALID));

console.log(decodedToken);
const userId = decodedToken.userId;

if (!userId) return res.status(statusCode.UNAUTHORIZED).send(util.fail(statusCode.UNAUTHORIZED, responseMessage.TOKEN_INVALID));

user = await userDB.getUserById(client, userId);

if (!user) return res.status(statusCode.UNAUTHORIZED).send(util.fail(statusCode.UNAUTHORIZED, responseMessage.NO_USER));

req.user = user;
} catch (error) {
console.log(error);
res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, responseMessage.INTERNAL_SERVER_ERROR));
} finally {
client.release();
}

next();
};

module.exports = { checkUser };
Loading

0 comments on commit c6a1f0e

Please sign in to comment.