Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] checkUser 미들웨어 구현 #9

Merged
merged 4 commits into from
Jan 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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