Skip to content

Commit

Permalink
Merge branch 'release/v1.0.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
HYOSITIVE committed Jun 24, 2023
2 parents a723aee + 84a013e commit 57e448e
Show file tree
Hide file tree
Showing 36 changed files with 874 additions and 1,028 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
echo "JWT_ALGORITHM=${{ secrets.JWT_ALGORITHM }}" >> .env.dev
echo "JWT_ACCESS_EXPIRE=${{ secrets.JWT_ACCESS_EXPIRE }}" >> .env.dev
echo "JWT_REFRESH_EXPIRE=${{ secrets.JWT_REFRESH_EXPIRE }} " >> .env.dev
echo "SENTRY_DSN=${{ secrets.DEV_SENTRY_DSN }} " >> .env.dev
echo "SENTRY_TRACES_SAMPLE_RATE=${{ secrets.DEV_SENTRY_TRACES_SAMPLE_RATE }} " >> .env.dev
- name: create .p8 file
run: |
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
echo "JWT_ALGORITHM=${{ secrets.JWT_ALGORITHM }}" >> .env.prod
echo "JWT_ACCESS_EXPIRE=${{ secrets.JWT_ACCESS_EXPIRE }}" >> .env.prod
echo "JWT_REFRESH_EXPIRE=${{ secrets.JWT_REFRESH_EXPIRE }} " >> .env.prod
echo "SENTRY_DSN=${{ secrets.PROD_SENTRY_DSN }} " >> .env.prod
echo "SENTRY_TRACES_SAMPLE_RATE=${{ secrets.PROD_SENTRY_TRACES_SAMPLE_RATE }} " >> .env.prod
- name: create .p8 file
run: |
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ iOS의 Share Extension, Android의 Intent Filter를 사용하여 홈 화면으
```
3. Bracket 사용 시 내부에 주석을 작성한다.
```javascript
if (a == 5) {
if (a === 5) {
// 주석
}
```
Expand All @@ -264,14 +264,14 @@ iOS의 Share Extension, Android의 Intent Filter를 사용하여 홈 화면으

``` javascript
// 괄호 사용 한칸 띄우고 사용한다.
if (left == true) {
if (left === true) {
return;
}
```
3. Bracket 양쪽 사이를 띄어서 사용한다.
``` javascript
// 띄어쓰기
if (a == 5) { // 양쪽 사이로 띄어쓰기
if (a === 5) { // 양쪽 사이로 띄어쓰기
return;
}
```
Expand Down
21 changes: 20 additions & 1 deletion functions/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,27 @@ const cors = require("cors");
const cookieParser = require("cookie-parser");
const hpp = require("hpp");
const helmet = require("helmet");
const Sentry = require('@sentry/node');
const errorHandler = require('../middlewares/errorHandler');

// initializing
const app = express();

Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: `${process.env.NODE_ENV}_app`,
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
new Sentry.Integrations.Express({ app }),
...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(),
],
tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE,
});

app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.tracingHandler());


// Cross-Origin Resource Sharing을 열어주는 미들웨어
// https://even-moon.github.io/2020/05/21/about-cors/ 에서 자세한 정보 확인
app.use(cors());
Expand All @@ -27,6 +44,8 @@ app.use(cookieParser());

// 라우팅: routes 폴더로 정리
app.use("/", require("./routes"));
app.use(Sentry.Handlers.errorHandler());
app.use(errorHandler);

// route 폴더에 우리가 지정할 경로가 아닌 다른 경로로 요청이 올 경우
// 잘못된 경로로 요청이 들어왔다는 메세지를 클라이언트에 보냄
Expand All @@ -52,6 +71,6 @@ module.exports = functions
console.log("\n\n", "[api]", `[${req.method.toUpperCase()}]`, req.originalUrl, req.body);

// 맨 위에 선언된 express app 객체를 리턴
// 요것이 functiobns/index.js 안의 api: require("./api")에 들어가는 것.
// 요것이 functions/index.js 안의 api: require("./api")에 들어가는 것.
return app(req, res);
});
79 changes: 34 additions & 45 deletions functions/api/routes/auth/reissueTokenPOST.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,49 @@ const statusCode = require('../../../constants/statusCode');
const responseMessage = require('../../../constants/responseMessage');
const db = require('../../../db/db');
const { userDB } = require('../../../db');
const functions = require('firebase-functions');
const slackAPI = require('../../../middlewares/slackAPI');
const jwtHandlers = require('../../../lib/jwtHandlers');
const { TOKEN_INVALID, TOKEN_EXPIRED } = require('../../../constants/jwt');
const asyncWrapper = require('../../../lib/asyncWrapper');

/**
* @route POST /auth/reissue
* @desc 토큰 재발급
* @access Public
*/

module.exports = async (req, res) => {
const { userId } = req.body;
const refreshToken = req.header("refresh-token");
module.exports = asyncWrapper(async (req, res) => {
const { userId } = req.body;
const refreshToken = req.header("refresh-token");

const decodedToken = jwtHandlers.verify(refreshToken);
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));
}
// 토큰 만료, 유효하지 않은 토큰 모두 강제 로그아웃 필요함
const decodedToken = jwtHandlers.verify(refreshToken);
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));
}
// 토큰 만료, 유효하지 않은 토큰 모두 강제 로그아웃 필요함

let client;
try {
client = await db.connect(req);
const user = await userDB.getUser(client, userId);
const dbConnection = await db.connect(req);
req.dbConnection = dbConnection;
const user = await userDB.getUser(dbConnection, userId);

// DB user 테이블의 Refresh Token과 클라이언트에게 받아 온 Refresh Token 비교
if (refreshToken == user.refreshToken) {
// Refresh Token 일치 : Aceess Token, Refresh Token 재발급, DB 업데이트
const newAccessToken = jwtHandlers.sign(user);
const newRefreshToken = jwtHandlers.signRefresh();
await userDB.updateRefreshToken(client, user.id, newRefreshToken);
const reissuedTokens = {
'accessToken' : newAccessToken,
'refreshToken' : newRefreshToken
};
res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.TOKEN_REISSUE_SUCCESS, reissuedTokens));
}
else {
// Refresh Token 불일치 : 강제 로그아웃
return res.status(statusCode.UNAUTHORIZED).send(util.fail(statusCode.UNAUTHORIZED, responseMessage.TOKEN_INVALID));
}
} catch (error) {
functions.logger.error(`[ERROR] [${req.method.toUpperCase()}] ${req.originalUrl}`, `[CONTENT] ${error}`);
console.log(error);
const slackMessage = `[ERROR] [${req.method.toUpperCase()}] ${req.originalUrl} ${req.user ? `uid:${req.user.userId}` : 'req.user 없음'} ${JSON.stringify(error)}`;
slackAPI.sendMessageToSlack(slackMessage, slackAPI.WEB_HOOK_ERROR_MONITORING);
res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, responseMessage.INTERNAL_SERVER_ERROR));
} finally {
client.release();
}
};
// DB user 테이블의 Refresh Token과 클라이언트에게 받아 온 Refresh Token 비교
if (refreshToken === user.refreshToken) {
// Refresh Token 일치 : Aceess Token, Refresh Token 재발급, DB 업데이트
const newAccessToken = jwtHandlers.sign(user);
const newRefreshToken = jwtHandlers.signRefresh();
await userDB.updateRefreshToken(dbConnection, user.id, newRefreshToken);
const reissuedTokens = {
'accessToken' : newAccessToken,
'refreshToken' : newRefreshToken
};
res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.TOKEN_REISSUE_SUCCESS, reissuedTokens));
}
else {
// Refresh Token 불일치 : 강제 로그아웃
return res.status(statusCode.UNAUTHORIZED).send(util.fail(statusCode.UNAUTHORIZED, responseMessage.TOKEN_INVALID));
}
});
149 changes: 62 additions & 87 deletions functions/api/routes/auth/signinPOST.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,118 +4,93 @@ const responseMessage = require("../../../constants/responseMessage");
const kakao = require("../../../lib/kakaoAuth");
const db = require("../../../db/db");
const { userDB } = require("../../../db");
const functions = require("firebase-functions");
const slackAPI = require("../../../middlewares/slackAPI");
const jwtHandlers = require("../../../lib/jwtHandlers");
const { modifyFcmToken } = require('../../../lib/pushServerHandlers');
const { getAppleRefreshToken } = require("../../../lib/appleAuth");
const asyncWrapper = require('../../../lib/asyncWrapper');

/**
* @route POST /auth/signin
* @desc 기존 유저 로그인
* @access Public
*/

module.exports = async (req, res) => {
module.exports = asyncWrapper(async (req, res) => {
const { fcmToken, kakaoAccessToken, firebaseUID, appleCode } = req.body;

const badRequestMessage = '[ERROR] POST /auth/signin - Bad Request';

if (!fcmToken || (!firebaseUID && !kakaoAccessToken)) {
// fcmToken이 없거나 firebaseUID와 kakaoAccessToken이 모두 없을 때 : 에러
slackAPI.sendMessageToSlack(badRequestMessage, slackAPI.WEB_HOOK_ERROR_MONITORING);
return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.NULL_VALUE));
}

if (firebaseUID && kakaoAccessToken) {
// firebaseUID와 kakaoAccessToken이 모두 있을 때 : 에러
slackAPI.sendMessageToSlack(badRequestMessage, slackAPI.WEB_HOOK_ERROR_MONITORING);
return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.OUT_OF_VALUE));
if ((!fcmToken || (!firebaseUID && !kakaoAccessToken)) // fcmToken이 없거나 firebaseUID와 kakaoAccessToken이 모두 없을 때
|| (firebaseUID && kakaoAccessToken) // firebaseUID와 kakaoAccessToken이 모두 있을 때
|| (firebaseUID && !appleCode)) { // firebaseUID는 있으나 appleCode 가 없을 때
const badRequestError = new Error();
badRequestError.statusCode = statusCode.BAD_REQUEST;
badRequestError.responseMessage = (firebaseUID && kakaoAccessToken)? responseMessage.OUT_OF_VALUE : responseMessage.NULL_VALUE;
throw badRequestError;
}

if (firebaseUID && !appleCode) {
// firebaseUID 는 있으나 appleCode 가 없을 때 : 에러
slackAPI.sendMessageToSlack(badRequestMessage, slackAPI.WEB_HOOK_ERROR_MONITORING);
return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.NULL_VALUE));
}
const dbConnection = await db.connect(req);
req.dbConnection = dbConnection;
let isAlreadyUser;

let client;
if (!firebaseUID) {
// firebaseUID가 없을 때 : 카카오 소셜 로그인
const firebaseUserData = await kakao.createFirebaseToken(kakaoAccessToken);
const { firebaseAuthToken, firebaseUserId } = firebaseUserData;
const kakaoUser = await userDB.getUserByFirebaseId(dbConnection, firebaseUserId);

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

let isAlreadyUser;
if (!kakaoUser) {
// 신규 사용자
isAlreadyUser = false;
return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.NO_USER, { isAlreadyUser }));
}
else {
// 기존 사용자
isAlreadyUser = true;
const accessToken = jwtHandlers.sign({ id: kakaoUser.id, idFirebase: kakaoUser.idFirebase });
const refreshToken = jwtHandlers.signRefresh();
const nickname = kakaoUser.nickname;

if (!firebaseUID) {
// firebaseUID가 없을 때 : 카카오 소셜 로그인
const firebaseUserData = await kakao.createFirebaseToken(kakaoAccessToken);
const { firebaseAuthToken, firebaseUserId } = firebaseUserData;
const kakaoUser = await userDB.getUserByFirebaseId(client, firebaseUserId);
const response = await modifyFcmToken(kakaoUser.mongoUserId, fcmToken);

if (!kakaoUser) {
// 신규 사용자
isAlreadyUser = false;
return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.NO_USER, { isAlreadyUser }));
if (response.status !== 204) {
return res.status(response.statusCode).send(util.fail(response.statusCode, responseMessage.PUSH_SERVER_ERROR));
}
else {
// 기존 사용자
isAlreadyUser = true;
const accessToken = jwtHandlers.sign({ id: kakaoUser.id, idFirebase: kakaoUser.idFirebase });
const refreshToken = jwtHandlers.signRefresh();
const nickname = kakaoUser.nickname;

const response = await modifyFcmToken(kakaoUser.mongoUserId, fcmToken);
await userDB.updateRefreshToken(dbConnection, kakaoUser.id, refreshToken);

if (response.status != 204) {
return res.status(response.statusCode).send(util.fail(response.statusCode, responseMessage.PUSH_SERVER_ERROR));
}
return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.SIGNIN_SUCCESS,
{ firebaseAuthToken, accessToken, refreshToken, nickname }));
};
}
else {
// kakaoAccessToken이 없을 때 (!kakaoAccessToken) : 애플 소셜 로그인
const appleUser = await userDB.getUserByFirebaseId(dbConnection, firebaseUID);

await userDB.updateRefreshToken(client, kakaoUser.id, refreshToken);

return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.SIGNIN_SUCCESS,
{ firebaseAuthToken, accessToken, refreshToken, nickname }));
};
}
if (!appleUser) {
// 신규 사용자
isAlreadyUser = false;
return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.NO_USER, { isAlreadyUser }));
}
else {
// kakaoAccessToken이 없을 때 (!kakaoAccessToken) : 애플 소셜 로그인
const appleUser = await userDB.getUserByFirebaseId(client, firebaseUID);

if (!appleUser) {
// 신규 사용자
isAlreadyUser = false;
return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.NO_USER, { isAlreadyUser }));
}
else {
// 기존 사용자
const accessToken = jwtHandlers.sign({ id: appleUser.id, idFirebase: appleUser.idFirebase });
const refreshToken = jwtHandlers.signRefresh();
const nickname = appleUser.nickname;
const firebaseAuthToken = "";
// 기존 사용자
const accessToken = jwtHandlers.sign({ id: appleUser.id, idFirebase: appleUser.idFirebase });
const refreshToken = jwtHandlers.signRefresh();
const nickname = appleUser.nickname;
const firebaseAuthToken = "";

// apple refresh token 발급
const appleRefreshToken = await getAppleRefreshToken(appleCode);
await userDB.updateAppleRefreshToken(client, appleUser.id, appleRefreshToken);
// apple refresh token 발급
const appleRefreshToken = await getAppleRefreshToken(appleCode);
await userDB.updateAppleRefreshToken(dbConnection, appleUser.id, appleRefreshToken);

const response = await modifyFcmToken(appleUser.mongoUserId, fcmToken);
const response = await modifyFcmToken(appleUser.mongoUserId, fcmToken);

if (response.status != 204) {
return res.status(response.statusCode).send(util.fail(response.statusCode, responseMessage.PUSH_SERVER_ERROR));
}

await userDB.updateRefreshToken(client, appleUser.id, refreshToken);

return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.SIGNIN_SUCCESS,
{ firebaseAuthToken, accessToken, refreshToken, nickname }));
};
}
} catch (error) {
functions.logger.error(`[ERROR] [${req.method.toUpperCase()}] ${req.originalUrl}`, `[CONTENT] ${error}`);
console.log(error);
const slackMessage = `[ERROR] [${req.method.toUpperCase()}] ${req.originalUrl} ${req.user ? `uid:${req.user.userId}` : "req.user 없음"} ${JSON.stringify(error)}`;
slackAPI.sendMessageToSlack(slackMessage, slackAPI.WEB_HOOK_ERROR_MONITORING);
if (response.status !== 204) {
return res.status(response.statusCode).send(util.fail(response.statusCode, responseMessage.PUSH_SERVER_ERROR));
}

res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR,responseMessage.INTERNAL_SERVER_ERROR));
} finally {
client.release();
await userDB.updateRefreshToken(dbConnection, appleUser.id, refreshToken);

return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.SIGNIN_SUCCESS,
{ firebaseAuthToken, accessToken, refreshToken, nickname }));
};
}
};
});
Loading

0 comments on commit 57e448e

Please sign in to comment.