페이스북 로그인 구현
- 회원정보를 보관하는 것은 회원입장에선 불편하고, 서비스 입장에선 부담되는 일입니다. 이런 문제를 해결하기 위해서 최근에는 페이스북이나 구글과 같은 기업들이 로그인 연동 기능을 제공한다.
- 이를 Federated Identity라고 하고 Passport.js를 이용하면 이를 쉽게 구현할 수 있다.
npm install -s passport-facebook
위의 코드를 입력하여 페이스북 로그인 구현을 위한 모듈을 다운로드한다.
var = FacebookStrategy = require('passport-facebook').Strategy;
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://www.example.com/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
// User.findOrCreate(..., function(err, user) {
// if (err) { return done(err); }
// done(null, user);
// });
}
));
그리고 위 두 코드를 passport을 구현시키는 코드 파일에 붙여줘야한다.
https://developers.facebook.com/
Facebook for Developers
Facebook for Developers와 사용자를 연결할 수 있는 코드 인공 지능, 비즈니스 도구, 게임, 오픈 소스, 게시, 소셜 하드웨어, 소셜 통합, 가상 현실 등 다양한 주제를 둘러보세요. Facebook의 글로벌 개발
developers.facebook.com
그 다음으로 페이스북 개발자 사이트에 들어가서 앱을 생성해야한다.
개발자 사이트에 들어가 로그인을 한 후 내 앱 메뉴를 클릭한다.
개개인의 정보들을 인증 한 후에 넘어간다.
인증 후에 앱 만들기를 클릭하면 앱 만들기를 할 수 있다.
로그인 기능을 설정하기 위해서 페이스북 로그인 설정하기를 클릭하고 웹 플렛폼 선택 후 임의의 URL을 적어주고 설정을 마친다.
왼쪽 메뉴에 기본 설정을 클릭해보면 페이스북 로그인 기능을 위해 사용될 '앱 시크릿 코드'를 받을 수 있다.
config/facebook.json에 심어둔 callbackURL을 페이스북 개발자 사이트 설정에서 위와 같이 리디렉션 URL에 붙여주고 저장해주어야 한다.
config/facebook.json
{
"clientID": "1440599276283262",
"clientSecret": 시크릿 코드,
"callbackURL": "http://localhost:3000/auth/facebook/callback"
}
lib/auth.js
module.exports = {
isOwner: function(request, response){
if(request.user){
return true;
} else{
return false;
}
}, statusUI: function(request, response){
var authStatusUI = `
<a href="/auth/login">login</a> |
<a href="/auth/register">Register</a> |
<a href="/auth/facebook">Login with Facebook</a>` //페이스북 로그인 UI 추가
if(this.isOwner(request, response)){
authStatusUI = `${request.user.displayName} | <a href="/auth/logout">logout</a>`;
}
return authStatusUI;
}
}
lib/passport.js
var db = require('../lib/db');
var bcrypt = require('bcrypt');
var shortid = require('shortid');
module.exports = function (app) {
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
FacebookStrategy = require('passport-facebook').Strategy; //페이스북 passport 기능 연동
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
var user = db.get('users').find({id:id}).value();
done(null, user);
});
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'pwd'
},
function (email, password, done) {
var user = db.get('users').find({
email:email
}).value();
if(user){
bcrypt.compare(password, user.password, function(err,result){
if(result){
return done(null, user, {
message: 'Welcome.'
});
} else {
return done(null, false, {
message: 'Password is not correct.'
});
}
});
}
else {
return done(null, false, {
message: 'There is no email.'
});
}
}
));
var facebookCredentials = require('../config/facebook.json'); //facebook.json과 연동
//facebook.json에 배열 안에 있는 값들을 생성
facebookCredentials.profileFields = ['id', 'emails', 'name', 'displayName'];
passport.use(new FacebookStrategy(facebookCredentials,
//아래 콜백함수가 페이스북 로그인을 구현되게끔하는 많은 과정을 함축해서 처리해준다.
function(accessToken, refreshToken, profile, done) {
console.log('FacebookStrategy', accessToken, refreshToken, profile);
//profile의 프로퍼티 중 emails의 첫번째 값이 페이스북 회원 email 값을 갖고 있음.
var email = profile.emails[0].value;
//페이스북 회원 email 값을 받은 변수 email을 사용하여 db.json에서 동일한
//이메일 정보를 찾는 값을 변수화한다.
var user = db.get('users').find({email:email}).value();
//페이스북 회원 이메일 값이 이미 user에 존재한다면 if문이 발동되어 db.json 안에 삽입된
//user.facebookId와 원래 있었던 보통 회원 정보인 profile.id와 일치하도록 하고
//db.json의 users에 email 값을 삽입한다. 즉 보통 회원 이메일과 페이스북
// 이메일과 중복되면 합쳐지도록 하여 하나의 같은 회원 정보를 갖게 된다.
if(user){
user.facebookId = profile.id;
db.get('users').find({email:email}).assign(user).write();
}
//이메일이 중복되지 않으면 아래와 같이 새로운 user 정보를 db.json에 삽입한다.
else {
user = {
id:shortid.generate(),
email:email,
displayName:profile.displayName,
facebookId:profile.id
}
db.get('users').push(user).write();
}
done(null, user);
// User.findOrCreate(..., function(err, user) {
// if (err) { return done(err); }
// done(null, user);
// });
}
));
//아래 코드는 facebook에서 제공하는 코드로서 페이스북 인증 로그인이 발동될때 자동으로
//페이스북으로 이동할 주소를 만들어주는 코드로 보면 된다.
app.get('/auth/facebook', passport.authenticate('facebook', {
scope:'email' //email 정보를 주어 사용자를 가린다.
}));
//아래 코드도 마찬가지로 페이스북 로그인 발동시 설정해준 callback URL이 발동되면 아래 코드
//가 실행되어 매우 복잡한 로그인 인증 과정을 처리해준다.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
successRedirect: '/',
failureRedirect: '/auth/login'
}));
return passport;
}
routes/auth.js
var express = require('express');
var router = express.Router();
var path = require('path');
var fs = require('fs');
var sanitizeHtml = require('sanitize-html');
var template = require('../lib/template.js');
var shortid = require('shortid');
var db = require('../lib/db');
var bcrypt = require('bcrypt');
module.exports = function (passport) {
router.get('/login', function (request, response) {
var fmsg = request.flash();
var feedback = '';
if (fmsg.error) {
feedback = fmsg.error[0];
}
var title = 'WEB - login';
var list = template.list(request.list);
var html = template.HTML(title, list, `
<div style="color:red;">${feedback}</div>
<form action="/auth/login_process" method="post">
<p><input type="text" name="email" placeholder="email"></p>
<p><input type="password" name="pwd" placeholder="password"></p>
<p>
<input type="submit" value="login">
</p>
</form>
`, '');
response.send(html);
});
router.post('/login_process',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/auth/login',
failureFlash: true,
successFlash: true
}));
router.get('/register', function (request, response) {
var fmsg = request.flash();
var feedback = '';
if (fmsg.error) {
feedback = fmsg.error[0];
}
var title = 'WEB - login';
var list = template.list(request.list);
var html = template.HTML(title, list, `
<div style="color:red;">${feedback}</div>
<form action="/auth/register_process" method="post">
<p><input type="text" name="email" placeholder="email"></p>
<p><input type="password" name="pwd" placeholder="password"></p>
<p><input type="password" name="pwd2" placeholder="password"></p>
<p><input type="text" name="displayName" placeholder="display name"></p>
<p>
<input type="submit" value="register">
</p>
</form>
`, '');
response.send(html);
});
router.post('/register_process', function (request, response) {
var post = request.body;
var email = post.email;
var pwd = post.pwd;
var pwd2 = post.pwd2;
var displayName = post.displayName;
if(pwd !== pwd2){
request.flash('error', 'Password must same!');
response.redirect('/auth/register');
} else {
bcrypt.hash(pwd, 10, function (err, hash) {
//일단 회원가입 폼에서 받아온 회원가입 email 값을 통해 db.json에서
//users에 같은 email 값을 가지고 있는 user을 찾는 값을 변수화 한다.
var puser = db.get('users').find({email:email}).value();
//이때 email값이 똑같은 값이 존재한다면 if문이 발동하여 아래와 같이
//puser의 프로퍼티 password, displayName에 동일했던 이메일의 패스워드와 displayNam 값을 붙여주기만한다.
if(puser){
puser.password = hash;
puser.displayName = displayName;
//여기가 중요한게 id가 puser.id인 값을 찾아 puser을 준다는 것은 중복된 데이터 생성을 막는거다.
//즉 이미 페이스북 로그인 정보를 받았다면 똑같은 페이스북 이메일로 보통 회원가입을 하면
//그 보통 회원가입한 user 정보 안에 페이스북 id만 추가된다.
db.get('users').find({id:puser.id}).assign(puser).write();
//이메일 중복이 없어 puser값이 없다면 else문이 발동되어
//아예 새로 통째로 user 데이터를 만들어준다.
} else {
var user = {
id: shortid.generate(),
email: email,
password: hash,
displayName: displayName
};
db.get('users').push(user).write();
}
request.login(user, function (err) {
console.log('redirect');
return response.redirect('/');
})
});
}
});
router.get('/logout', function (request, response) {
request.logout();
request.session.save(function () {
response.redirect('/');
});
});
return router;
}
페이스북과 연동되며 동시에 lowdb의 db.json 파일에 페이스북의 회원정보도 들어오게 되었다.
또 만약 페이스북으로 로그인 한 후 페이스북 로그인이 아닌 보통의 회원가입으로 로그인 했었던 페이스북 이메일과 동일한 이메일을 통해 가입했다면 동일한 user 정보를 쓰게끔 구현하였다.
'프로그래밍 개발 > Express' 카테고리의 다른 글
Express - 비밀번호 암호화 (0) | 2021.01.19 |
---|---|
Express - 다중 사용자 구현(회원가입) (0) | 2021.01.19 |
Express - passport.js로 로그인 기능 적용하기 (0) | 2021.01.18 |
Express -passport.js 설치하기 (0) | 2021.01.18 |
Express - session을 이용한 로그인 인증 구현 (0) | 2021.01.16 |
댓글