csrf.js
import bcrypt from "bcrypt";
const csrf = async (req, res, next) => {
if (req.method === "GET") {
return next();
}
const csrfHeader = req.get("csrf-token");
if (!csrfHeader) {
return res.status(403).json({ message: "CSRF ERROR - 1" });
}
const isTrue = await bcrypt.compare("csrf", csrfHeader);
if (!isTrue) {
return res.status(403).json({ message: "CSRF ERROR - 2" });
}
next();
};
export default csrf;
app.js
import express from "express";
import helmet from "helmet";
import morgan from "morgan";
import cors from "cors";
import tweetController from "./tweet/tweet.controller.js";
import authController from "./auth/auth.controller.js";
import { config } from "../config.js";
import sequelize from "../database.js";
import csrf from "./middleware/csrf.js";
const app = express();
app.use(csrf);
app.use(express.json());
app.use(morgan("dev"));
app.use(helmet());
app.use(cors());
app.use("/tweet", tweetController);
app.use("/auth", authController);
app.use((req, res, next) => {
res.sendStatus(404);
});
app.use((err, req, res, next) => {
res.sendStatus(500);
});
sequelize.sync().then(() => {
app.listen(config.port, () => {
console.log("Server On...");
});
});
auth.controller.js
import express from "express";
import { body } from "express-validator";
import { auth } from "../middleware/auth.js";
import { validate } from "../middleware/validator.js";
import * as authService from "./auth.service.js";
const authController = express.Router();
const loginValidation = [
body("username").trim().notEmpty().withMessage("username을 입력해주세요."),
body("password")
.trim()
.isLength({ min: 5 })
.withMessage("비밀번호는 최소 5글자 이상 입력해주세요."),
validate,
];
const signUpValidation = [
...loginValidation,
body("name").trim().notEmpty().withMessage("이름이 비어있습니다."),
body("email")
.isEmail()
.normalizeEmail()
.withMessage("이메일 형식에 맞지 않습니다"),
validate,
];
authController.post("/signup", signUpValidation, authService.signup);
authController.post("/login", loginValidation, authService.login);
authController.get("/me", auth, authService.me);
authController.get("/csrf", authService.csrfToken);
export default authController;
auth.service.js
import * as userRepository from "../user/user.repository.js";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { config } from "../../config.js";
const createToken = (id) => {
return jwt.sign({ id }, config.jwtSecretKey, {
expiresIn: config.jwtExpires,
});
};
export const signup = async (req, res) => {
const { username, password, name, email } = req.body;
const user = await userRepository.findByUsername(username);
if (user) {
return res.status(409).json({ message: `이미 가입된 유저입니다.` });
}
const hashed = await bcrypt.hash(password, config.bcryptSaltRounds);
const createdUserId = await userRepository.createUser({
username,
password: hashed,
name,
email,
});
const token = createToken(createdUserId);
res.status(201).json({ token, username });
};
export const login = async (req, res) => {
const { username, password } = req.body;
const user = await userRepository.findByUsername(username);
if (!user) {
return res
.status(401)
.json({ message: "아이디 혹은 비밀번호가 틀렸습니다." });
}
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
return res
.status(401)
.json({ message: "아이디 혹은 비밀번호가 틀렸습니다." });
}
const token = createToken(user.id);
res.status(200).json({ token, username });
};
export const me = async (req, res) => {
const user = await userRepository.findById(req.userId);
if (user) {
return res
.status(200)
.json({ token: req.token, username: user.username });
}
res.status(404).json({ message: "유저가 존재하지 않습니다." });
};
export const csrfToken = async (req, res) => {
const csrfToken = await bcrypt.hash("csrf", 1);
res.status(200).json({ csrfToken });
};