공식 사이트: http://www.passportjs.org/
passport는 인증을 편리하게 구성할 수 있는 모듈로, 기본적인 로그인부터 여러 사이트의 다양한 소셜 로그인을 비슷하게 구현할 수 있습니다. passport에서 많은 Strategy(전략, 템플릿과 비슷)를 제공하고 있어 개발자가 별 다른 작업 없이 이 전략을 가져와서 구현할 수 있습니다. 처음엔 이해하기 어렵지만 알아두면 활용하기 편한 모듈입니다. 간편하게 사용자를 인증하고 별 다른 로직없이 로그인한 유저의 정보(req.user
)를 불러올 수 있는 등 다양하고 편리한 기능을 사용할 수 있습니다.
기본적인 아이디, 패스워드를 이용한 로그인(local Strategy)의 인증 과정을 간략하게 설명하겠습니다.
req.user
에 정보를 삽입 (deserializeUser() 함수)로그인 요청을 처리할 때 passport.authenticate()
함수를 사용하여 1
2
3
4
과정이 모두 실행되고, 그 외의 요청에서 세션 저장소에 passport를 통해 저장된 id가 있다면 4
번이 미들웨어로 실행됩니다.
세션을 설정하는 코드입니다. MongoDB를 세션 저장소를 설정할 때 connect-mongo
모듈을 사용합니다.
// app.js
const session = require('express-session');
const MongoStore = require('connect-mongo');
app.use(
session({
secret: '시크릿 키',
resave: false,
saveUninitialized: true,
store: MongoStore.create({
mongoUrl: 'MongoDB 주소, mongodb://~~~',
})
})
);
로그인 전략을 설정하는 부분입니다. 아이디와 패스워드가 일치하는지 확인하고 done
을 통해 serializeUser()
함수로 넘길지 결정합니다.
// 기본 로그인 전략
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
passport.use(
new LocalStrategy(
{ // passport 내에서 아이디를 username, 패스워드를 password라는 이름으로 변수 사용
usernameField: 'id', // id -> username
passwordField: 'pw' // pw -> password
}, // 위의 id, pw를 아래 함수에서 username, password으로 사용
function (username, password, done) {
User.findOne({ id: username }, (err, user) => {
if (user) {
if (password == user.password) {
return done(null, user); // serializeUser 함수로 user 정보를 넘김
} else {
return done(null, false, { message: 'Incorrect password.' }); // 인증 실패
}
} else {
return done(null, false, { message: 'Incorrect ID' });
}
});
}
)
);
serializeUser는 done(null, user._id)
를 통해 _id
를 세션 저장소에 저장하고 deserializeUser로 넘어갑니다.
deserializeUser는 클라이언트가 어떤 요청시 항상 실행되는 미들웨어로, 세션 저장소에서 _id
를 가져와 유저 정보를 DB에서 찾아 req.user
를 통해 사용할 수 있도록 합니다.
initialize와 session을 express의 미들웨어로 사용합니다.
passport.serializeUser(function (user, done) {
done(null, user._id); // 세션 저장소에 id를 저장
});
passport.deserializeUser(function (id, done) {
User.findOne({ _id: id }, (err, user) => {
done(null, user); // 세션 저장소에 저장된 id값을 DB에서 조회하여 req.user에 담음
});
});
app.use(passport.initialize()); // 새로운 요청마다 passport를 초기화
app.use(passport.session()); // 새로운 요청마다 세션을 변경
로그인 시 passport.authenticate
함수를 사용하여 인증 미들웨어를 실행합니다.
로그아웃 시 req.session.destroy()
를 사용하여 세션을 지웁니다.
router.post('/login',
passport.authenticate('local', { failureRedirect: '/auth' }),
(req, res) => {
res.redirect('/');
}
);
router.get('/logout', async (req, res) => {
try {
if (req.session) {
req.session.destroy();
}
res.redirect(req.headers.referer);
} catch (err) {
res.redirect(req.headers.referer);
}
});
passport 로컬 전략이 익숙해지면 소셜 로그인을 간편하게 구현할 수 있으며, 인증과 허가가 분리된 OAuth가 적용되어 있습니다. 시간이 된다면 다음 포스팅 주제로 다루도록 하겠습니다! 😀