c3epmos 2020. 1. 15. 10:34

passport란?

페이스북, 구글과 같은 기존의 SNS 서비스 계정을 이용해 로그인을 하도록하는 연동 기능을 수행하는 모듈

로컬, 페이스북, 구글 등 다양한 서비스를 이용할 수 있으나, 각각 필요한 서비스 별로 설치 해야한다.

예를 들어, 로컬과 카카오톡을 사용할 경우 아래와 같이 설치한다.

npm i passport passport-local passport-kakao bcrypt
view raw txt.txt hosted with ❤ by GitHub

app.js

passportConfig(passport);
// app.js에서 설정 관련 코드는 app.use route 위에다가 배치
app.use(expressSession({
maxAge: 1000 * 60 * 60,
secret : process.env.SECRET,
resave: false,
saveUninitialized: true,
cookie: {secure: false}
}));
app.use(passport.initialize()); // passport 구동
app.use(passport.session()); // 세션 연결
app.use('/', indexRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
view raw app.js hosted with ❤ by GitHub

module/passport/index.js

const local = require('./localStrategy');
const kakao = require('./kakaoStrategy');
const models = require('../../models');
module.exports = (passport) => {
// strategy로부터 넘겨받은 정보들을 이용해 세션에 저장한다.
passport.serializeUser((user, done) => {
// 규칙 : 두 번째 인자인 done에 null, data 순으로 넣는다.(정상)
done(null, user.userIdx);
});
// 페이지 접근마다 deserialize를 이용해 세션을 확인하고 내부의 정보들을 활용한다.
passport.deserializeUser((idx, done) => {
done(null, idx);
});
local(passport);
kakao(passport);
};
view raw index.js hosted with ❤ by GitHub

module/passport/kakaoStrategy.js

const KakaoStrategy = require('passport-kakao').Strategy;
const models = require('../../models');
require('dotenv').config();
module.exports = async (passport) => {
passport.use(
// kakao strategy는 kakao-login이라는 이름으로 정한다.
'kakao-login',
// strategy 생성
new KakaoStrategy({
// kakao 로부터 받은 APP KEY
clientID: process.env.KAKAO_ID,
// strategy가 끝난 후 callback url
callbackURL: '/auth/kakao/signin/callback',
// 여기서 카카오톡으로부터 profile을 받고, done으로 다음 함수에 넘긴다.
}, async (accessToken, refreshToken, profile, done) => {
try {
// profile의 id를 이용해 계정이 존재하는지 확인한다.
const exUser = await models.User.findOne({
where: {
snsId: profile.id,
provider: 'kakao'
}
});
if (exUser) {
// 존재한다면 찾은 계정 정보(서버 DB)를 찾아서
// passport/index.js(serializeUser)에 반환한다.
done(null, exUser);
} else {
// 존재하지 않으면, 계정을 생성하고 새 계정 정보를
// passport/index.js(serializeUser)에 반환한다.
const newUser = await models.User.create({
email: profile._json.kakao_account.email,
password: profile.id,
name: profile._json.properties.nickname,
snsId: profile.id,
provider: 'kakao'
});
done(null, newUser);
}
} catch (error) {
console.log(error);
done(error);
}
}));
};

module/passport/Log.js

const utils = require('../utils/utils');
const responseMessage = require('../utils/responseMessage');
const statusCode = require('../utils/statusCode');
// 로그인 된 상태인지 확인하는 미들웨어
exports.isLoggedIn = (req, res, next) => {
// req.isAuthenticated()가 true 라면 OK
if (req.isAuthenticated()) {
next();
} else {
res.status(statusCode.BAD_REQUEST).send(utils.successFalse(responseMessage.NEED_SIGN_IN));
}
};
// 로그인 된 상태가 아닌지 확인하는 미들웨어
exports.isNotLoggedIn = (req, res, next) => {
// req.isAuthenticated()가 false 라면 OK
if (!req.isAuthenticated()) {
next();
} else {
res.redirect('/success');
}
};
view raw Log.js hosted with ❤ by GitHub

routes/Auth/index.js

// 카카오 로그인 페이지 접속을 위한 route
router.get('/kakao', (req, res) => {
res.render('kakao');
})
// passport.authenticate 메소드를 이용해 kakaoStrategy를 호출한다.
// 세션이 존재하지 않는 상태인지 isNotLoggedIn으로 확인한다.
router.get('/kakao/signin', isNotLoggedIn, passport.authenticate('kakao-login'));
// 카카오 로그인을 끝내고 처리하는 콜백함수
// 세션이 존재하지 않는 상태인지 isNotLoggedIn으로 확인한다.
router.get('/kakao/signin/callback', isNotLoggedIn, passport.authenticate('kakao-login', {
successRedirect: '/success',
failureRedirect: '/fail'
}));
view raw index.js hosted with ❤ by GitHub

 

문제

app.js에서 passport initialize(), session() 메소드를 호출했음에도 불구하고, 초기화 관련 오류가 발생했다.

 

해결

app.js에서 설정 관련 코드 라우트 호출 함수 위에서 배치하는 것이 좋다. request는 일반적으로 라우트로 넘어가는데, 그럴 경우 설정 관련 함수인 초기화 함수를 그 아래에 배치하면 이 함수를 읽지도 않고 넘어가는 것으로 사료된다.