Node.js에서 passport 모듈을 활용한 로그인

조상균·2021년 4월 24일
1

Node.js

목록 보기
8/10
post-thumbnail

공식 사이트: http://www.passportjs.org/

🧷 passport 소개

passport는 인증을 편리하게 구성할 수 있는 모듈로, 기본적인 로그인부터 여러 사이트의 다양한 소셜 로그인을 비슷하게 구현할 수 있습니다. passport에서 많은 Strategy(전략, 템플릿과 비슷)를 제공하고 있어 개발자가 별 다른 작업 없이 이 전략을 가져와서 구현할 수 있습니다. 처음엔 이해하기 어렵지만 알아두면 활용하기 편한 모듈입니다. 간편하게 사용자를 인증하고 별 다른 로직없이 로그인한 유저의 정보(req.user)를 불러올 수 있는 등 다양하고 편리한 기능을 사용할 수 있습니다.

🏄‍ passport 인증 과정

기본적인 아이디, 패스워드를 이용한 로그인(local Strategy)의 인증 과정을 간략하게 설명하겠습니다.

  1. 통신하게 되면 클라이언트에는 쿠키, 서버는 클라이언트에 대응하는 세션 저장소를 기본적으로 생성
  2. 클라이언트가 인증을 담당하는 API로 로그인 요청
  3. 서버는 DB를 조회해 아이디 패스워드가 일치하는지 확인 (Strategy 클래스)
  4. 정상적인 유저라면 세션 저장소에 해당 사용자의 ID를 저장 (serializeUser() 함수)
  5. 세션 저장소에 사용자 ID가 있다면 DB를 조회하여 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' }); 
				}
			});
		}
	)
);

serializeUserdone(null, user._id)를 통해 _id를 세션 저장소에 저장하고 deserializeUser로 넘어갑니다.
deserializeUser는 클라이언트가 어떤 요청시 항상 실행되는 미들웨어로, 세션 저장소에서 _id를 가져와 유저 정보를 DB에서 찾아 req.user를 통해 사용할 수 있도록 합니다.
initializesession을 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가 적용되어 있습니다. 시간이 된다면 다음 포스팅 주제로 다루도록 하겠습니다! 😀

profile
백엔드 개발을 공부하고 있습니다.

0개의 댓글