diff --git a/src/middlewares/auth.middleware.js b/src/middlewares/auth.middleware.js new file mode 100644 index 0000000..7567648 --- /dev/null +++ b/src/middlewares/auth.middleware.js @@ -0,0 +1,38 @@ +import jwt from 'jsonwebtoken'; + +export default async function (req, res, next) { + try { + const { authorization } = req.cookies; + if (!authorization) throw new Error('토큰이 존재하지 않습니다.'); + + const [tokenType, token] = authorization.split(' '); + if (tokenType !== 'Bearer') + throw new Error('토큰 타입이 일치하지 않습니다.'); + + const decodedToken = jwt.verify(token, process.env.TOKEN_SECRET_KEY); + const id = decodedToken.id; + const account = getAccounts().find((account) => account.id == loginId); + + if (!account) { + res.clearCookie('authorization'); + throw new Error('토큰 사용자가 존재하지 않습니다.'); + } + + req.account = account; + + next(); + } catch (error) { + res.clearCookie('authorization'); + + switch (error.name) { + case 'TokenExpiredError': + return res.status(401).json({ message: '토큰이 만료되었습니다.' }); + case 'JsonWebTokenError': + return res.status(401).json({ message: '토큰이 조작되었습니다.' }); + default: + return res + .status(401) + .json({ message: error.message ?? '비정상적인 요청입니다.' }); + } + } +} diff --git a/src/models/account.model.js b/src/models/account.model.js new file mode 100644 index 0000000..6a3421a --- /dev/null +++ b/src/models/account.model.js @@ -0,0 +1,9 @@ +const accounts = []; + +export const addAccount = (account) => { + accounts.push(account); +}; + +export const getAccounts = () => { + return accounts; +}; diff --git a/src/routes/accounts.router.js b/src/routes/accounts.router.js new file mode 100644 index 0000000..80c2a4b --- /dev/null +++ b/src/routes/accounts.router.js @@ -0,0 +1,65 @@ +import express from 'express'; +import bcrypt from 'bcrypt'; +import jwt from 'jsonwebtoken'; +import { addAccount, getAccounts } from '../models/account.model.js'; + +const router = express.Router(); +const regex = /^[A-Za-z0-9]*$/; + +/** 사용자 회원가입 API **/ +router.post('/sign-up', async (req, res, next) => { + try { + const { loginId, password } = req.body; + const isExistUser = getAccounts().find((account) => account.id == loginId); + console.log(getAccounts()); + + if (isExistUser) + return res.status(409).json({ message: '이미 존재하는 ID입니다.' }); + else if (!regex.test(loginId)) + return res + .status(400) + .json({ message: 'ID는 영어와 숫자만 사용할 수 있습니다.' }); + else if (password.length < 6) + return res + .status(400) + .json({ message: '비밀번호는 6자 이상이어야 합니다.' }); + + const hashedPassword = await bcrypt.hash(password, 10); + + // DB 저장부 + addAccount({ id: loginId, password: hashedPassword }); + + return res.status(201).json({ message: '회원가입이 완료되었습니다.' }); + } catch (error) { + next(error); + } +}); + +/** 사용자 로그인 API **/ +router.post('/sign-in', async (req, res, next) => { + try { + const { loginId, password } = req.body; + // DB 호출부 + const account = getAccounts().find((account) => account.id == loginId); + + if (!account) + return res.status(401).json({ message: '존재하지 않는 ID입니다.' }); + else if (!(await bcrypt.compare(password, account.password))) + return res.status(401).json({ message: '비밀번호가 일치하지 않습니다.' }); + + const token = jwt.sign( + { + id: account.id, + }, + process.env.TOKEN_SECRET_KEY, + ); + + res.cookie('authorization', `Bearer ${token}`); + + return res.status(200).json({ message: '로그인 성공' }); + } catch (error) { + next(error); + } +}); + +export default router;