Passport 는 정말 친절한 라이브러리다. 단 몇 줄의 코드로, 소셜 로그인을 구현할 수 있다. Passport 로 소셜 로그인을 구현하기 위해 아래 명령어로 패키지를 설치한다.
$ npm i express express-session passport passport-facebook passport-google-oauth20 passport-kakao passport-naver
설치하는 패키지들을 간단하게 설명하면
express
: 웹 서버 구현을 도와주는 프레임워크express-session
: express 프레임워크에서 session을 사용하기 위한 라이브러리passport
: passport 코어 라이브러리passport-facebook
: facebook 로그인에 필요한 라이브러리passport-google-oauth20
: google 로그인에 필요한 라이브러리passport-kakao
: kakao 로그인에 필요한 라이브러리passport-naver
: naver 로그인에 필요한 라이브러리// app.js
const express = require('express')
const http = require('http')
const app = express()
const server = http.createServer(app)
const PORT = process.env.PORT || 5000
server.listen(PORT, () => console.log(`Server is runngin on ${PORT}`))
위 코드를 작성하고 실행하면 5000번 포트로 웹 서버가 실행된다.
소셜 로그인에 필요한 ID, Secret, Redirect URL을 .env
파일에서 관리 할 것이다. 아래 명령어로 dotenv 라이브러리를 설치 해준다.
$ npm i dotenv
// app.js
const dotenv = require('dotenv')
dotenv.config()
위와 같이 설정 해주면, 코드에서 process.env['key']
로 .env
에 있는 값을 접근 할 수 있다.
필자는 app.js
가 더러워지는 것을 별로 좋아하지 않아, 설정 관련된 코드는 최대한 파일로 분리 시킨다.
// app.js
/**
* 세션 세팅
*/
const configureSession = require('./config/session')
configureSession(app)
// config/session.js
const session = require('express-session')
module.exports = (app) => {
app.use(
session({
secret: process.env['SESSION_SECRET'],
cookie: { maxAge: 60 * 60 * 1000 },
resave: false,
saveUninitialized: true,
})
)
}
세션 만료 기간은 1시간으로 설정 했으며, secret 은 .env
에서 가져온다.
resave 는 세션을 언제나 저장할 지 정하는 값입니다. express-session doc 에서는 이 값을 false 로 하는 것을 권장하고 필요에 따라 true로 설정합니다.
saveUninitialized 는 세션이 저장되기 전에 uninitialized 상태로 미리 만들어서 저장합니다.
// app.js
/**
* passport 세팅
*/
const configurePassport = require('./config/passport')
configurePassport(app)
// config/passport.js
const passport = require('passport')
module.exports = (app) => {
app.use(passport.initialize())
app.use(passport.session())
/**
* Serialize
*/
passport.serializeUser((user, done) => {
done(null, user)
})
/**
* Deserialize
*/
passport.deserializeUser((user, done) => {
done(null, user)
})
}
serializeUser 는 로그인에 성공했을 때 호출된다. user 의 값은, 뒤에서 추가 할 각 전략의 결과 값을 done 으로 보낸 값이다. 보통은 소셜 로그인에 성공한 사용자의 프로필 정보다. serializeUser 에서 done 을 호출하게 되면, session에 사용자 정보다 저장되고, 두 번째 인자로 전달한 user가 deserializeUser로 전달된다.
deserializeUser 는 서버에 요청이 있을 때마다 호출된다. done 의 두 번째 인자로 user를 전달하게 되면 req.user로 user의 값을 접근할 수 있게 된다.
passport는 LocalStrategy, GoogleStrategy ... 등 다양한 로그인 전략을 제공한다. 여기서는 GoogleStrategy 만 확인하고, 전체 코드는 git 에서 확인할 수 있다.
// config/passport.js
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20').Strategy
module.exports = (app) => {
...
/**
* Google Strategy
*/
passport.use(
new GoogleStrategy(
{
clientID: process.env['GOOGLE_CLIENT_ID'],
clientSecret: process.env['GOOGLE_CLIENT_SECRET'],
callbackURL: process.env['GOOGLE_CALLBACK'],
},
function (accessToken, refreshToken, profile, done) {
done(null, profile)
}
)
)
}
다른 소셜 로그인 모두 구글 로그인과 패턴이 비슷하다. 구글 로그인만 한 번 해보면 다른 것들은 힘들지 않게 할 수 있을 것이다. 여기서 done 을 호출하게 되면 두 번째 인자 profile이 serializeUser 로 전달된다.
// app.js
...
/**
* Routing
*/
const authRouter = require('./routes/auth')
app.use('/auth', authRouter)
app.get('/', (req, res) => {
/**
* req.user가 있는 경우는 소셜 로그인에 성공한 경우
* passport에 의해 user가 주입됨 (deserialize 확인)
*/
if (req.user) {
res.send(`
<h3>Login Success</h3>
<a href="/auth/logout">Logout</a>
<p>
${JSON.stringify(req.user, null, 2)}
</p>
`)
} else {
res.send(`
<h3>Node Passport Social Login</h3>
<a href="/auth/login/google">Login with Google+</a>
<a href="/auth/login/facebook">Login with Facebook</a>
<a href="/auth/login/naver">Login with Naver</a>
<a href="/auth/login/kakao">Login with Kakao</a>
`)
}
})
// routes/auth.js
const express = require('express')
const passport = require('passport')
const router = express.Router()
router.get('/login/google', passport.authenticate('google', { scope: ['profile'] }))
router.get(
'/login/google/callback',
passport.authenticate('google', { failureRedirect: '/auth/login' }),
(req, res) => {
res.redirect('/')
}
)
module.exports = router
/
로 접근 했을 때 로그인 및 로그아웃을 테스트 할 수 있는 간단한 HTML 을 전달하고 있다. 여기서 req.user
의 값을 통해 로그인 여부를 확인 할 수 있다.
/login/google
으로 접근하면, passport ㅡ를 거쳐 google login api 를 호출 한다.
/login/google/callback
은 Google Developer 에서 등록한 callback URL 로 성공, 실패 여부에 대한 callback router다.
로그아웃 코드는 매우 간단하다.
// routes/auth.js
...
router.get('/logout', (req, res) => {
req.logout()
res.redirect('/')
})
passport로 부터 설정된 logout 메소드를 호출 해주고, 그 이후에는 서비스에 알맞게 처리 해주면 된다. 여기서 세션이 제대로 지워지지 않는다는 문제가 종종 발생한다고 하는데, 그럴 땐 아래 코드를 적용하면, 세션이 정상적으로 만료되는 것을 확인 할 수 있을 것이다.
// routes/auth.js
...
router.get('/logout', (req, res) => {
req.session.destroy((err) => {
req.logout()
res.redirect('/')
})
})
이상으로 Express 환경에서 passport.js
라이브러리를 이용하여, 구글 로그인을 하는 방법을 살펴봤다. 카카오, 네이버 등 다양한 소셜 로그인이 있는데, 전반적인 컨셉은 모두 동일하여 따로 다루지는 않았다. 각 Developer t사이트에서 Client ID 와 Client Secrety Key 만 잘 받아 오면 크게 문제 없이 구현 할 수 있을 것이다.
안녕하세요. 글 잘읽었습니다. 해당 글에서 logout 메서드와 session 설정은 자체 로그인 즉 소셜로그인이 아닌 자체적으로 만든 로그인에서 적용되는걸로 알고있습니다. 그러면 logout 을 한다해도 자체적으로 만든 session 이랑 쿠키만 삭제가되고 소셜 서비스에서는 로그아웃이 안된걸로 아는데 맞을까요?