Nodejs

 

생활 코딩 강의 목록 :  https://opentutorials.org/module/3590

인프런 강의 목록 https://www.inflearn.com/course/node-js-express/dashboard

 

 

 

이 수업은 CC 라이센스를 따르고 있으며, 아래 링크 에서도 볼 수 있습니다.
Node.js-Express https://opentutorials.org/course/3370
쿠키와 인증 https://opentutorials.org/course/3387
세션과 인증 https://opentutorials.org/course/3400 passpor.js
https://opentutorials.org/course/3402
다중 사용자 https://opentutorials.org/course/3411 google login https://opentutorials.org/course/3413 facebook login
https://opentutorials.org/course/3414

 

 

WEB2 - Node.js 수업의 예제 코드 바로가기

 

 

 

소스 :  https://github.com/braverokmc79/Nodejs-master-passpot-google-and-facebook

 

$ git clone https://github.com/passport/todos-express-starter.git google-tutorial

 

 

Node.Js - Express (생활코딩) - 4 Passport.Js Passport Mysql , Passport 비밀번호 암호화, Passport 회원 가입 , Passport 로그인 , Passport 인증

 

 

 

 

mkcert 을 사용하여 로컬 개발에 HTTPS를 사용하는 방법

 

 

1. mkcert  사용  및 윈도우 설치

https://github.com/FiloSottile/mkcert

 

윈도우에 mkcert 를 사용하려면 먼저  CHOCOLATEY 설치해야 한다.

https://chocolatey.org/install

cmd 창을 관리자 권한으로  열고 다음 명령어로 

 

1) Chocolatey 설치하기

요구사항

  • Windows 7+/ Windows Server 2003+
  • Powershell v2+ (온라인 설치 시 v3+)
  • .Net Framework 4+ (온라인 설치 시 4.5+)

cmd.exe로 설치하기

  1. cmd.exe를 관리자 권한으로 실행합니다.

  2. 다음 명령줄을 실행합니다.

 

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command " [System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

 

 

PowerShell.exe로 설치하기

PowerShell로 설치할 때에는 실행 정책(Execution Policy)상 제한이 걸리지 않도록 설정하는 과정이 추가적으로 필요할 수 있습니다. 실행 정책에 관해서는 간단하게 Bypass 옵션을 사용하셔도 되고, 좀 더 보안을 신경쓰신다면 AllSigned 옵션을 사용하셔도 됩니다.

  1. PowerShell.exe를 관리자 권한으로 실행합니다.

  2. Get-ExecutionPolicy 명령어로 실행 정책을 확인합니다. 만약 Restricted가 출력된다면, Set-ExecutionPolicy AllSigned 나, Set-ExecutionPolicy Bypass -Scope Process를 입력하여 제한을 풀어줍니다.

  3. 이제 다음의 명령줄을 실행합니다.

 

 

[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

 

그 외의 방법으로 설치하기

chocolatey 공식 홈페이지를 참고하세요.

 

 

2 ) Windows mkcert 설치

On Windows, use Chocolatey

choco install mkcert

or use Scoop

scoop bucket add extras
scoop install mkcert

 

mkcert  설치를 했으면 cmd 를 닫은 후에 새로  열어야 한다.

 

 

 

3)로컬 환경에 대한 인증서 생성

 

다음은 로컬 환경에 대한 인증서를 만들어야 한다.

 

  • 옵션으로 추가한 example.com 도메인에서 사용할 수 있는 인증서 발급 
mkcert -key-file key.pem -cert-file cert.pem example.com *.example.com

 

또는

 

  • 옵션으로 추가한 localhost, 127.0.0.1(IPv4), ::1(IPv6)에서 사용할 수 있는 인증서 발급
mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1

로컬을 c:/  확인해보면 cert.pem, key.pem이라는 파일이 생성된 것을 확인할 수 있다.

 

 

https 디렉토리를 만든 후 옮겨 놓았다.

 

 

4) node.js https 모듈 이용

const https = require('https');
const fs = require('fs');

https
  .createServer(
    {
      key: fs.readFileSync(__dirname + '/key.pem', 'utf-8'),
      cert: fs.readFileSync(__dirname + '/cert.pem', 'utf-8'),
    },
    function (req, res) {
      res.write('Congrats! You made https server now :)');
      res.end();
    }
  )
  .listen(3000);

 

 

 

5)express.js를 이용

const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

https
  .createServer(
    {
      key: fs.readFileSync(__dirname + '/key.pem', 'utf-8'),
      cert: fs.readFileSync(__dirname + '/cert.pem', 'utf-8'),
    },
    app.use('/', (req, res) => {
      res.send('Congrats! You made https server now :)');
    })
  )
  .listen(3000);

 

 

6)

http 서버 사용:

다음과 같이 서버를 시작합니다({PATH/TO/CERTIFICATE...} 대체).

http-server -S -C {PATH/TO/CERTIFICATE-FILENAME}.pem -K {PATH/TO/CERTIFICATE-KEY-FILENAME}.pem

-S는 HTTPS로 서버를 실행하고 -C는 인증서를 설정하고 -K는 키를 설정합니다.

 

React 개발 서버 사용:

package.json을 다음과 같이 편집하고 {PATH/TO/CERTIFICATE...}를 대체합니다.

"scripts": {
"start": "HTTPS=true SSL_CRT_FILE={PATH/TO/CERTIFICATE-FILENAME}.pem SSL_KEY_F

 

 

webpack의 devServer 설정

devServer: {
  ... // 기존 설정들
  https: {
    key: "./*-key.pem", // 생성된 파일의 이름을 입력해주세요
    cert: "./*.pem"
  }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Facebook Login

 

 

87. 수업소개

 

 

https://www.passportjs.org/tutorials/facebook/

 

소개

이 튜토리얼 은 완료해야 할 작업을 추적하기 위한 앱인 Todos 를 구축하는 과정을 안내합니다. 사람들은 Facebook 계정으로 로그인하여 개인의 할 일 목록에 액세스합니다.

사용자가 Facebook으로 로그인할 수 있는 페이지를 만드는 것으로 시작합니다. 그런 다음 세션 관리 및 로그아웃 기능을 추가합니다.

당신이 어디로 가고 있는지 알고 싶다면 다음은 최종 결과의 예입니다: https://github.com/passport/todos-express-facebook

이 앱을 빌드하려면 Node.js 및 Git 이 있는 작업 개발 환경과 선택한 편집기 및 터미널이 필요합니다. 아직 설정하지 않았다면 잠시 시간을 내어 이러한 도구를 설정하십시오.

시작하자!

할 일 목록을 작성하는 데 필요한 모든 스캐폴딩이 있는 스타터 앱을 복제합니다.

$ git clone https://github.com/passport/todos-express-starter.git facebook-tutorial

이제 라는 디렉토리가 facebook-tutorial있습니다. 그것으로 변경하십시오.

 

 

88. 프로젝트 등록

 

 

페이스북 로그인 설정하기

방문자가 Facebook 계정으로 내 사이트에 가입하고 로그인할 수 있도록 Facebook 로그인을 설치할 수 있습니다. 이 도움말에서는 Facebook 로그인을 설치하는 방법을 단계별로 안내합니다.

주의: 2019년 3월부터 Facebook 정책 변경에 따라 Facebook 로그인을 사용하기 위해선 SSL 보안서버 인증서를 설치해야 합니다. 자세한 설치방법은 SSL 보안서버 인증서 설치하기 도움말에서 확인할 수 있습니다.

 

https://imweb.me/faq?mode=view&category=29&category2=47&idx=71636

 

 

 

 

89. 비밀정보 관리

 

 

 

 

 

90. Resource owner 인증절차

 

 

 

 

 

91. id 획득 절차

 

 

 

 

 

91. email scope

 

 

 

 

92. 회원생성

 

 

 

 

 

93. 회원정보관리

 

 

 

 

 

 

94. 수업을 마치며

 

 

 

페이스북으로 리디렉션

 

login.js

<h1>Sign in</h1>
<a class="button facebook" href="/login/federated/facebook">Sign in with Facebook</a>

Sign in

Sign in with Facebook

 

 

이전 섹션 에서는 사용자에게 Facebook으로 로그인하라는 메시지를 표시하는 페이지를 만들었습니다. 이 섹션에서는 사용자가 버튼을 클릭할 때 Facebook으로 리디렉션하는 경로를 추가합니다. 제자리에 넣어야 할 몇 가지 다른 조각이 있기 때문에 아직 작동하지 않습니다. 후속 단계에서는 누락된 기능을 추가하고 이 자습서가 끝날 때까지 모든 것이 작동합니다.

설치 passport및 passport-facebook전략을 종속성으로 사용합니다.

 

$ npm install passport
$ npm install passport-facebook



 

아래 2행에서 앱의 데이터베이스와 새로 설치된 패키지를 routes/auth.js엽니 다 .require()require('express')

 

var express = require('express');
var passport = require('passport');
var FacebookStrategy = require('passport-facebook');
var db = require('../db');

 

다음으로 사용자가 "Facebook으로 로그인"을 클릭할 때 리디렉션할 경로를 추가합니다. 

에서 계속해서 경로 routes/auth.js아래의 12행에 다음 코드를 추가 /login합니다

 

router.get('/login/federated/facebook', passport.authenticate('facebook'));

 

서버를 시작하여 로그인을 시도하십시오.

 

$ npm start

 

http://localhost:3000 을 열고 "로그인"을 클릭한 다음 "Facebook으로 로그인"을 클릭합니다.

 

이전 섹션에서는 Facebook 에 앱을 등록했습니다. 이 섹션에서는 등록 중에 얻은 정보로 Passport를 구성합니다.

먼저 .envFacebook에서 가져온 클라이언트 ID와 Secret을 저장할 파일을 만듭니다.

$ touch .env

 

그런 다음 클라이언트 ID와 암호를 추가합니다. 의 내용은 .env다음과 같아야 합니다.

FACEBOOK_CLIENT_ID=__INSERT_CLIENT_ID_HERE__
FACEBOOK_CLIENT_SECRET=__INSERT_CLIENT_SECRET_HERE__

 

* env  설치  및 사용 설정

 $npm i dotenv

app.js

const dotenv = require("dotenv")
dotenv.config();

 

참조 : .env 파일 관리부터 dotenv & DB랑 NodeJS 연결하기

 

 

 

Facebook에서 얻은 클라이언트 ID와 비밀은 명시된 곳에 삽입해야 합니다.

6번째 줄에 다음 코드를 열고 routes/auth.js추가하여 FacebookStrategy.

 

passport.use(new FacebookStrategy({
  clientID: process.env['FACEBOOK_CLIENT_ID'],
  clientSecret: process.env['FACEBOOK_CLIENT_SECRET'],
  callbackURL: '/oauth2/redirect/facebook',
  state: true
}, function verify(accessToken, refreshToken, profile, cb) {
  db.get('SELECT * FROM federated_credentials WHERE provider = ? AND subject = ?', [
    'https://www.facebook.com',
    profile.id
  ], function(err, row) {
    if (err) { return cb(err); }
    if (!row) {
      db.run('INSERT INTO users (name) VALUES (?)', [
        profile.displayName
      ], function(err) {
        if (err) { return cb(err); }

        var id = this.lastID;
        db.run('INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)', [
          id,
          'https://www.facebook.com',
          profile.id
        ], function(err) {
          if (err) { return cb(err); }
          var user = {
            id: id,
            name: profile.displayName
          };
          return cb(null, user);
        });
      });
    } else {
      db.get('SELECT * FROM users WHERE id = ?', [ row.user_id ], function(err, row) {
        if (err) { return cb(err); }
        if (!row) { return cb(null, false); }
        return cb(null, row);
      });
    }
  });
}));

 

 

 

 

 

 

 

 

프로젝트 적용

 

express + 넌적스 적용 =>  https://macaronics.net/index.php/m03/nodejs/view/1952

 

 

1) main.js

const express = require('express')
const fs = require('fs');
const bodyParser = require('body-parser')
const compression = require('compression')
const logger = require('morgan');
const session = require('express-session')
const MySQLStore = require('express-mysql-session')(session);
const flash = require('connect-flash');
const app = express()

const helmet = require('helmet')
const dotenv = require("dotenv")
const db = require('./lib/db');


const https = require('https');


dotenv.config();

app.use(helmet())
app.use(express.static('public'))
app.use(bodyParser.urlencoded({ extended: false }));
app.use(compression());


// session DB 저장 방식 - session 테이블이 자동 생성되고  세션이 passport의해 저장 된다.
app.use(session({
  secret: '12312dajfj23rj2po4$#%@#',
  resave: false,
  saveUninitialized: true,
  store: new MySQLStore({
    host: 'localhost',
    port: 3306,
    user: 'opentutorials',
    password: '1111',
    database: 'opentutorials'
  })
}));

app.use(flash());
const passport = require("./lib/passport")(app);


app.get('*', (req, res, next) => {
  db.query("SELECT * FROM TOPICS ", [], function (err, result) {
    req.list = result;
    next();
  });
});





const indexRouter = require("./routes/index");
const authRouter = require("./routes/auth")(passport);
const topicRouter = require("./routes/topic");

app.use("/", indexRouter);
app.use('/auth', authRouter);
app.use('/topic', topicRouter);



app.use(function (req, res) {
  res.status(400).send("Sorry cant find that!");
});

app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

// app.listen(port, () => {
//   console.log(`Example app listening on port ${port}`)
// })

const port = 3000
https
  .createServer(
    {
      // key: fs.readFileSync(__dirname + '/key.pem', 'utf-8'),
      // cert: fs.readFileSync(__dirname + '/cert.pem', 'utf-8'),
      key: fs.readFileSync('C:/https/localhost-key.pem', 'utf-8'),
      cert: fs.readFileSync('C:/https/localhost.pem', 'utf-8'),
    },
    app.use('/', (req, res) => {
      res.send('Congrats! You made https server now :)');
    })
  )
  .listen(port);



 

 

2) routes/auth.js

const express = require('express');
const router = express.Router();
const template = require('../lib/template.js');
const auth = require("../lib/auth.js");
const db = require("../lib/db");
global.crypto = require('crypto')

module.exports = function (passport) {

  //로그인 페이지
  router.get("/login", (req, res) => {
    const fmsg = req.flash();
    console.log("fmsg : ", fmsg);
    let feedback = '';
    if (fmsg.error) {
      feedback = fmsg.error[0];
    }
    const title = 'WEB - login';
    const list = template.list(req.list);
    const 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="password" placeholder="password"></p>                                 
              <p>
                <input type="submit" value="login">

              </p>
              <p>
              <a class="button google" style="padding: 8px; text-decoration: none; color: #fff;background: #a22f2f;border-radius: 50px;"
                       href="/auth/login/federated/google">Sign in with Google</a>
   
              <a class="button facebook" style="padding: 8px; text-decoration: none; color: #fff; background: #3f51b5;border-radius: 50px;"
              href="/auth/login/federated/facebook">Sign in with Facebook</a>
               </p>
            </form>
          `, '', auth.statusUI(req, res));
    res.send(html);
  });




  // 로그인 처리 - 1) 성공 및 실패 페이지 설정 및 flash 사용여부 설정하기
  router.post('/login_process', passport.authenticate('local', {
    successRedirect: '/',
    failureRedirect: '/auth/login',
    failureFlash: true,
    successFlash: true
  }));



  //1.구글로그인 처리 - 로그인 버튼 클릭시
  router.get('/login/federated/google', passport.authenticate('google'));


  //2.구글로그인 처리 - 콜백 반환
  router.get('/oauth2/redirect/google', passport.authenticate('google', {
    successRedirect: '/',
    failureRedirect: '/auth/login',
    failureFlash: true,
    successFlash: true
  }));


  //1.페이스북그인 처리 - 로그인 버튼 클릭시
  router.get('/login/federated/facebook', passport.authenticate('facebook', { scope: 'email' }));


  //2.페이스북그인  처리 - 콜백 반환
  router.get('/oauth2/redirect/facebook', passport.authenticate('facebook', {
    successRedirect: '/',
    failureRedirect: '/auth/login',
    failureFlash: true,
    successFlash: true
  }));


  //로그 아웃 처리
  router.get('/logout', function (req, res, next) {
    req.logout(function (err) {
      if (err) { return next(err); }

      req.session.destroy(function (err) {
        res.redirect("/");
      })
    });
  });


  //회원가입페이지
  router.get('/signup', function (req, res, next) {
    const fmsg = req.flash();
    let feedback = '';
    console.log("회원 가입페이지 : ", fmsg);
    if (fmsg.error) {
      feedback = fmsg.error[0];
    }

    const title = 'WEB - login';
    const list = template.list(req.list);
    const html = template.HTML(title, list, `
      <div style="color:red">${feedback}</div>
           <h1>Sign up</h1>
      <form action="/auth/signup_process" method="post">
          <section>
              <label for="username">이름</label>
              <input id="username" name="username" type="text" placeholder="이름" required>
          </section>
         <section>
              <label for="email">이메일</label>
              <input id="email" name="email" type="email" placeholder="이메일" required>
          </section>          
          <section>
              <label for="password">비밀번호</label>
              <input id="password" name="password" type="password" autocomplete="new-password" placeholder="비밀번호" required>
          </section>
          <section>
              <label for="password">비밀번호확인</label>
              <input id="password" name="pw2" type="password" autocomplete="new-password" placeholder="비밀번호확인" required>
          </section>          
          <button type="submit">Sign up</button>
      </form>
          `, '', auth.statusUI(req, res));
    res.send(html);
  });

  //회원가입처리
  router.post('/signup_process', function (req, res, next) {
    if (req.body.password !== req.body.pw2) {
      req.flash("error", "비밀번호가 일치 하지 않습니다.");
      return res.redirect("/auth/signup");
    }
    crypto.randomBytes(16, (error, buf) => {
      const salt = buf.toString("base64");

      crypto.pbkdf2(req.body.password.trim(), salt, 310000, 32, 'sha256', function (err, hashedPassword) {
        console.log("회원가입 데이터 : ", req.body.password, salt, hashedPassword.toString('hex'));

        if (err) { return next(err); }
        db.query('INSERT INTO users (username,email, hashed_password, salt) VALUES (?, ?, ?, ?)', [
          req.body.username,
          req.body.email,
          hashedPassword.toString('hex'),
          salt
        ], function (err, results, fields) {

          if (err) {
            req.flash("error", err.toString());
            return res.redirect('/auth/signup');
          }

          var user = {
            id: results.insertId,
            username: req.body.username
          };

          console.log("등록한 insertId :", results.insertId);

          req.login(user, function (err) {
            if (err) { return next(err); }
            req.flash("success", "회원가입을 축하합니다.");
            res.redirect('/');
          });


        });
      });


    });
  });

  return router;
}

 

 


★현재 프로젝트에서는 적용하지 않았으나,

 passReqToCallback: true 옵션값을 넣어 req  파라미터값을 사용할 수 있다.

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    passReqToCallback: true
}, function verify(req, email, password, done) {
    console.log('local-join callback called');
    console.log(email, password, done);
}
));

 

 

 

3) lib/passport.js

const passport = require("passport");
const crypto = require('crypto');
const db = require("../lib/db");
const LocalStrategy = require('passport-local');
const GoogleStrategy = require('passport-google-oidc');
const FacebookStrategy = require('passport-facebook');


module.exports = function (app) {
    app.use(passport.initialize());
    app.use(passport.session());

    passport.serializeUser(function (user, cb) {
        console.log("로그인 처리시 최초 한번  passport.serializeUser 호출 user 값  :", user);
        process.nextTick(function () {
            cb(null, { id: user.id, email: user.email, username: user.username });
        });
    });

    // 로그인 성공되면 passport.deserializeUser  매번 실행 처리된다
    passport.deserializeUser(function (user, cb) {
        console.log("deserializeUser  :", user);
        process.nextTick(function () {
            return cb(null, user);
        });
    });


    // 로그인 처리 - 2) passport 로그인
    passport.use(new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password'
    }, function verify(email, password, cb) {


        db.query('SELECT * FROM users WHERE email = ?', [email], function (err, row) {
            if (err) { return cb(err); }
            if (!row[0]) { return cb(null, false, { message: '등록된 email이 없습니다.' }); }
            if (!row[0].salt) return cb(null, false, { message: '소셜 로그인으로 등록된 유저입니다.' })

            crypto.pbkdf2(password, row[0].salt, 310000, 32, 'sha256', function (err, hashedPassword) {
                console.log("로그인 데이터 : ", password, row[0].salt, hashedPassword.toString('hex'));
                console.log("로그인 DB암호: ", row[0].hashed_password);

                if (err) { return cb(err); }
                if (row[0].hashed_password !== hashedPassword.toString('hex')) {
                    console.log("비밀번호 오류");
                    //flash 에 저장 처리됨  - "flash":{"error":["비밀번호가 일치하지 않습니다."]}}
                    return cb(null, false, { message: '비밀번호가 일치하지 않습니다.' });
                }


                console.log("로그인 성공");
                return cb(null, row[0], { message: '로그인 성공' });
            });
        });

    }));


    //구글 - 로그인 처리
    passport.use(new GoogleStrategy({
        clientID: process.env['GOOGLE_CLIENT_ID'],
        clientSecret: process.env['GOOGLE_CLIENT_SECRET'],
        callbackURL: '/auth/oauth2/redirect/google',
        scope: ['profile', 'email']
    }, function verify(issuer, profile, cb) {

        console.log("1.구글 로그인 profile: ", profile);


        db.query('SELECT * FROM federated_credentials WHERE provider = ? AND subject = ?', [
            "Google",
            profile.id
        ], function (err, row) {
            if (err) { return cb(err); }

            console.log("2. 구글 로그인 등록 처리: ", row.length);
            //1.federated_credentials 테이블에 등록되어 있지 않으면 신규등록처리
            if (row.length === 0) {

                db.query('SELECT * FROM users WHERE email = ?', [profile.emails[0].value], function (err, results) {
                    console.log("등록된 이메일이 존재 results : ", results);
                    //등록된 이메일이 존재하면은 로그인 처리
                    if (results.length > 0) {
                        //1) User 테이블에  등록되어 있다면 federated_credentials 테이블에만 등록후 로그인 처리
                        db.query('INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)', [
                            results[0].id, 'Google', profile.id], function (err) {
                                //구글 페이스북 등 여러 소셜에 대한
                                //user_id 중복 오류가 나올수 있다. 따라서,  에러 상관 없이 로그인 처리
                                return cb(null, results[0]);
                            });


                    } else {
                        //2) User 테이블 및   federated_credentials 테이블 모두 등록 되어 있지 않다면 모두 등록후 로그인 처리
                        db.query('INSERT INTO users (username, email) VALUES (?, ?)', [
                            profile.displayName, profile.emails[0].value
                        ], function (err, results, fields) {
                            if (err) return cb(err);

                            const id = results.insertId;
                            db.query('INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)', [
                                id, "Google", profile.id
                            ], function (err) {
                                if (err) { return cb(err); }
                                const user = {
                                    id: id,
                                    username: profile.displayName
                                };
                                return cb(null, user);
                            });
                        });

                    }

                });

            } else {
                //2.federated_credentials 테이블 등록되어 있다면 바로 로그인처리
                console.log("row.user_id : ", row[0].user_id);
                db.query('SELECT * FROM users WHERE id = ?', [row[0].user_id], function (err, row) {
                    if (err) { return cb(err); }
                    if (!row[0]) { return cb(null, false); }
                    return cb(null, row[0]);
                });
            }
        });

    }));


    //페이스북 로그인
    passport.use(new FacebookStrategy({
        clientID: process.env['FACEBOOK_CLIENT_ID'],
        clientSecret: process.env['FACEBOOK_CLIENT_SECRET'],
        callbackURL: '/auth/oauth2/redirect/facebook',
        state: true,
        profileFields: ['id', 'emails', 'name', 'displayName'],
    }, function verify(accessToken, refreshToken, profile, cb) {

        console.log(" 페이스북 로그인 ", profile);

        db.query('SELECT * FROM federated_credentials WHERE provider = ? AND subject = ?', [
            'Facebook',
            profile.id
        ], function (err, row) {
            if (err) { return cb(err); }

            console.log(" row.length ", row.length, profile.emails[0].value);

            //1.federated_credentials 테이블에 등록되어 있지 않으면 신규등록처리
            if (row.length === 0) {

                db.query('SELECT * FROM users WHERE email = ?', [profile.emails[0].value], function (err, results) {
                    console.log("등록된 이메일이 존재 results : ", results);

                    if (results.length > 0) {

                        //1) User 테이블에  등록되어 있다면 federated_credentials 테이블에만 등록후 로그인 처리
                        db.query('INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)', [
                            results[0].id, 'Facebook', profile.id], function (err) {
                                //구글 페이스북 등 여러 소셜에 대한
                                //user_id 중복 오류가 나올수 있다. 따라서,  에러 상관 없이 로그인 처리
                                return cb(null, results[0]);
                            });

                    } else {

                        //2) User 테이블 및   federated_credentials 테이블 모두 등록 되어 있지 않다면 모두 등록후 로그인 처리
                        db.query('INSERT INTO users (username, email) VALUES (?, ?)', [
                            profile.displayName, profile.emails[0].value
                        ], function (err, results, fields) {
                            if (err) return cb(err);

                            const id = results.insertId;
                            db.query('INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)', [
                                id, 'Facebook', profile.id
                            ], function (err) {
                                if (err) { return cb(err); }
                                const user = {
                                    id: id,
                                    username: profile.displayName
                                };
                                return cb(null, user);
                            });
                        });
                    }

                });


            } else {
                //2.federated_credentials 테이블 등록되어 있다면 바로 로그인처리
                db.query('SELECT * FROM users WHERE id = ?', [row[0].user_id], function (err, row) {
                    if (err) { return cb(err); }
                    if (!row[0]) { return cb(null, false); }
                    return cb(null, row[0]);
                });
            }
        });
    }));

    return passport;
}

 

 

https 로 테스트 할것

 

https://localhost:3000/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

재주를 다 쓰지 말고 남겨 두었다가 조물주에게 돌려주어라. 봉록(俸祿)을 다 쓰지 말고 남겨 두었다가 조정에 돌려주어라. 재물을 다 쓰지 말고 남겨 두었다가 백성에게 돌려주어라. 복을 다 누리지 말고 남겨 두었다가 자손에게 돌려주어라. -왕참정의 사류명(四留銘)

댓글 ( 4)

댓글 남기기

작성