Passport

  • 
    

Introduction

  • Node.js에서 인증을 위한 미들웨어인 passport.js를 사용하는 방법부터 주의할 점을 요약해 보았다 다시 passport.js (이하 패스포트)가 왜 나왔는지 짧게 얘기하면,

모던 웹 앱에서는 인증 방식이 굉장히 다양하다. 소셜 네트워킹이 증가하면서 페이스북이나 트위터로 인증하는 것이 인기있는 방법이 되었다. 이런 SNS나 OAuth를 제공해주는 API를 제공하는 서비스들은 이제 접근을 제어하는 방식으로 토큰 기반의 증명서를 요구하곤 한다.

패스포트는 각 앱이 구글로 가입할거냐, 페북으로 가입할거냐, 로컬에서 직접 가입할거냐 등 인증 요구사항을 가지는 것을 인지하고 전략 (strategies)으로 알려져 있는 인증 메커니즘을 각 모듈로 패키지화 해서 제공하고 있다. 즉, 앱은 패스포트에서 지원하는 전략을 선택해 의존성 없이 독립적으로 이용가능하다.

그래서 인증이라는 기능의 복잡성에도 불구하고 코드는 복잡해지지 않는다.

필수 모듈 설치

  • yarn add cookie-parser cookie-session express-session morgan connect-flash pug

  • yarn add dotenv

  • yarn add passport passport-local passport-facebook passport-kakao passport-naver passport-google-oauth20

  • yarn add passport-daum-oauth2 passport-twitter-oauth2 passport-cognito-oauth2 passport-instagram passport-linkedin-oauth2

  • 쿠키를 쉽게 추출할 수 있도록 도와주는 미들웨어
    var express = require('express');
    var cookieParser = require('cookie-parser');

    var app = express();
    app.use(cookieParser());

    app.get('/', function(req, res) {
        console.log('Cookies: ', req.cookies)
    })
    app.listen(8080)
  • 쿠키 기반의 스토리지를 구현하며, 하나의 세션 키가 아니라 세션 전체를 쿠키에 직렬화
    var session = require('cookie-session')
    var express = require('express')
    var app = express()

    var expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
    app.use(session({
        name: 'session',
        keys: ['key1', 'key2'],
        cookie: {
            secure: true,
            httpOnly: true,
            domain: 'example.com',
            path: 'foo/bar',
            expires: expiryDate
        }
    }))

express-session

  • express-session 은 Express 프레임워크에서 세션을 관리하기 위해 필요한 미들웨어
    var session = require('express-session');

    app.use(session({
    secret: '@#@$MYSIGN#@$#$',
    resave: false,
    saveUninitialized: true
    }));
  • secret – 쿠키를 임의로 변조하는것을 방지하기 위한 값 입니다. 이 값을 통하여 세션을 암호화 하여 저장합니다.
  • resave – 세션을 언제나 저장할 지 (변경되지 않아도) 정하는 값입니다. express-session documentation에서는 이 값을 false 로 하는것을 권장하고 필요에 따라 true로 설정합니다.
  • saveUninitialized – 세션이 저장되기 전에 uninitialized 상태로 미리 만들어서 저장합니다.

세션 초기 설정 (initialization)

    app.get('/', function(req, res){
        sess = req.session;
    });

세션 변수 설정

    app.get('/login', function(req, res){
        sess = req.session;
        sess.username = "velopert"
    });

세션 변수 사용

    app.get('/', function(req, res){
        sess = req.session;
        console.log(sess.username);
    });

세션 제거

    req.session.destroy(function(err){
    // cannot access session here
    });

morgan

  • 로그 기록을 남기는 morgan
    app.use(morgan('dev')); //morgan: 요청에 대한 정보를 콘솔에 기록
    stream
    combined
    common
    dev
    short
    tiny
    Tokens

connect-flash

  • 일회성 메시지들을 웹 브라우저에 나타냄
  • connect-flash 미들웨어는 cookie-parser와 express-session을 사용하므로 이들보다는 뒤로 위치해야 한다.
  • connect-flash은 휘발성으로 한번 실행되면 세션에 저장된 값이 없어집니다.

pug

  • Node Express Template Engine
  • HTML을 간단하게 표현해서 가독성이 좋다.
  • 마크업 문법보다 코드량이 적어 생산성이 좋아진다.
  • JS 연산 결과를 쉽게 보여줄 수 있다.
  • 정적인 부분과 동적인 부분을 따로 할 수 있다.

dotenv

  • dotenv 패키지를 통해 환경변수 파일을 외부에 만들고, 관리
  • 특히, 깃허브 등에 오픈소스로 프로젝트를 공개할때, DB 계정 정보를 소스코드 내에 하드코딩하지 않고, 외부 환경변수 파일에 작성하고, .gitignore 을 통해 제외하면 안전하다.
    프로젝트 루트경로에 process.env 파일을 생성
    require('dotenv').config();

    SERVER_PORT=3000
    DB_HOST=localhost
    DB_USER=root
    DB_PASSWORD=password

    db.connect({
        host: process.env.DB_HOST,
        username: process.env.DB_USER,
        password: process.env.DB_PASSWORD
    }); //DB 연결

passport-local

  • 이것은 우리가 흔히 사용하는 Username/Password 를 이용해서 쿠키-세션 으로 인증하게 해주는 passport Strategy 이다

Passport 설정

  • server.js
  var passport = require('passport') //passport module add
    , LocalStrategy = require('passport-local').Strategy;
  var cookieSession = require('cookie-session');
  var flash = require('connect-flash');

  app.use(cookieSession({
    keys: ['node_yun'],
    cookie: {
      maxAge: 1000 * 60 * 60 // 유효기간 1시간
    }
  }));
  app.use(flash());
  app.use(passport.initialize());
  app.use(passport.session());

  passport.initialize(), passport.session() 통해서 passport를 미들뒈어로 등록시킵니다. cookieSession을 Request 객체를 통해 Session을 핸들링할 수 있게 설정합니다. 만료기간 및 쿠키 키 값은 각자의 프로젝트에 맞게 설정하시면 됩니다.
  • login.html
  <form action="/login" method="post" name="frm_login" id="frm_login">
      <input type="text" name="username" />
      <input type="password" name="password"/>
      <input type="submit"  value="Login"/>
  </form>
  • server.js Router 설정
  var passport = require('passport')
    , LocalStrategy = require('passport-local').Strategy;

  router.post('/login', passport.authenticate('local', {failureRedirect: '/login', failureFlash: true}), // 인증 실패 시 401 리턴, {} -> 인증 스트레티지
    function (req, res) {
      res.redirect('/home');
    });

    login.html에서 post로 전송되면 이쪽에서 캐치하고 다음 작업을 진행하게 됩니다. passport.authenticate를 local strategy로 호출합니다. 이 호출은 아래에서 설명하겠습니다. failureRedirect를 통해서 로그인 실패 시 어디로 리다이렉트 할 것인지를 설정하고, 만약 로그인을 성공하게 되면 res.redirect를 통해 home으로 리다이렉트 시킵니다.
  • LocalStrategy
  passport.use(new LocalStrategy({
    usernameField: 'username',
    passwordField: 'password',
    passReqToCallback: true //인증을 수행하는 인증 함수로 HTTP request를 그대로  전달할지 여부를 결정한다
  }, function (req, username, password, done) {
    if(username === 'user001' && password === 'password'){
      return done(null, {
        'user_id': username,
      });
    }else{
      return done(false, null)
    }
  }));
  • serializeUser

  passport.serializeUser(function (user, done) {
    done(null, user)
  });

  로그인에 성공할 시 serializeUser 메서드를 통해서 사용자 정보를 Session에 저장하게 됩니다. 본 예제에는 “‘user_id’: username” 의값이 user에 들어가고 이 값을 Session에 저장하게 됩니다.
  • deserializeUser
  passport.deserializeUser(function (user, done) {
    done(null, user);
  });

  로그인에 성공하게 되면 Session정보를 저장을 완료했기에 이제 페이시 접근 시마다 사용자 정보를 갖게 Session에 갖게 됩니다. 인증이 완료되고 페이지 이동시 deserializeUser 메서드가 호출되는 것을 로그를 찍어 보시면 확인할 수 있습니다.

로그인 유저 판단

  • isAuthenticated()
  var isAuthenticated = function (req, res, next) {
    if (req.isAuthenticated())
      return next();
    res.redirect('/login');
  };

  router.get('/myinfo', isAuthenticated, function (req, res) {
    res.render('myinfo',{
      title: 'My Info',
      user_info: req.user
    })
  });

  • Logout
  router.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
  });