유저 인증 관련 처리는 passport
를 이용해서 처리했습니다.
passport
는 strategy
라는 단어를 이용해서 각 로그인 전략을 구현하도록 도와주는 라이브러리입니다.
현재는 local
전략만 구현을 한 상태이고, 프론트단을 만들고 나서 kakao
, naver
, facebook
로그인 전략도 구현할 예정입니다.
간단하게 말하면
1. 로그인 엔드포인트로 요청 발생
2. 전송된 데이터로 유저 인증
3. 인증 성공 시 serializeUser
를 통해서 서버의 세션에 유저의 식별자만 저장 ( 저장하는 곳은 memory
file
, radis
등 )
4. 인증 성공 시 req.login()
에서 세션쿠키를 만들어서 브라우저에게 전달
5. 로그인 이후 다음 요청부터 세션쿠키를 받아서 deserializeUser
에서 로그인한 유저 정보를 req.user
에 넣어줌
6. req.user
를 이용해서 로그인했는지, 했다면 정보는 무엇인지 판단 및 사용
import passport from "passport";
import passportLocalConfig from "./local.js";
import db from "../models/index.js"
const { User } = db;
const passportConfig = () => {
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser(async (_id, done) => {
try {
const user = await User.findOne({ where: { _id } });
done(null, user);
} catch (error) {
console.error("deserializeUser >> ", error);
done(error)
}
});
passportLocalConfig();
}
export default passportConfig;
import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import db from "../models/index.js";
import bcrypt from "bcrypt";
const { User } = db;
const passportLocalConfig = () => passport.use(
new LocalStrategy(
{
usernameField: "id",
passwordField: "password",
},
async (id, password, done) => {
try {
const user = await User.findOne({ where: { id } });
if (!user) {
return done(null, false, "존재하지 않는 아이디입니다.");
}
const result = await bcrypt.compare(password, user.password);
if (result) {
return done(null, user);
}
return done(null, false, "비밀번호가 틀렸습니다.");
} catch (error) {
console.error("passport local >> ", error);
return done(error);
}
}
)
);
export default passportLocalConfig;
import express from "express";
import passport from "passport";
import db from "../models/index.js";
import { isLoggedIn, isNotLoggedIn } from "../middleware/index.js"
const router = express.Router();
const { User, Post } = db;
router.post("/", isNotLoggedIn, (req, res, next) => {
passport.authenticate("local", (error, user, message) => {
// 서버측 에러 ( 조건검사 도중에 에러 )
if (error) {
console.error("POST /auth >> ", error);
return res.status(500).json({ message: "서버에서 알 수 없는 에러가 발생했습니다. 잠시후에 다시 시도해주세요" });
}
// 클라이언트측 에러 ( 아이디 or 비밀번호 불일치 )
if (message) {
return res.status(403).json({ message });
}
return req.login(user, async loginError => {
if (loginError) {
console.error("POST /user/login loginError >> ", loginError);
return res.status(500).json({ message: "서버에서 알 수 없는 에러가 발생했습니다. 잠시후에 다시 시도해주세요" });
}
// 유저와 유저와 관련된 정보까지 모아서 찾음
const fullUser = await User.findOne({
attributes: ["_id", "name", "createdAt"],
where: { _id: user._id },
include: [{ model: Post }, { model: User, as: "Followers" }, { model: User, as: "Followings" }],
});
return res.status(204).json({ message: "로그인에 성공했습니다.", user: fullUser });
});
})(req, res, next);
});
이미 알고 있었고, 정리해뒀던 내용이라 만드는데 많은 시간이나 큰 비용이 들지는 않았습니다.
조금 걱정되는 부분은 OAuth
를 통한 로그인 전략들은 아직 구현해 보지 않았기 때문에 걱정되며 유저 테이블 구조도 변경될 것 같다고 생각됩니다.