Javascript/Node.js
Passport
c3epmos
2020. 1. 15. 10:34
passport란?
페이스북, 구글과 같은 기존의 SNS 서비스 계정을 이용해 로그인을 하도록하는 연동 기능을 수행하는 모듈
로컬, 페이스북, 구글 등 다양한 서비스를 이용할 수 있으나, 각각 필요한 서비스 별로 설치 해야한다.
예를 들어, 로컬과 카카오톡을 사용할 경우 아래와 같이 설치한다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| npm i passport passport-local passport-kakao bcrypt |
app.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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)); | |
| }); |
module/passport/index.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| }; |
module/passport/kakaoStrategy.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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'); | |
| } | |
| }; |
routes/Auth/index.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 카카오 로그인 페이지 접속을 위한 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' | |
| })); |
문제
app.js에서 passport initialize(), session() 메소드를 호출했음에도 불구하고, 초기화 관련 오류가 발생했다.
해결
app.js에서 설정 관련 코드 라우트 호출 함수 위에서 배치하는 것이 좋다. request는 일반적으로 라우트로 넘어가는데, 그럴 경우 설정 관련 함수인 초기화 함수를 그 아래에 배치하면 이 함수를 읽지도 않고 넘어가는 것으로 사료된다.