[cookbook] Passport.js OAuth #1 : Twitter, OAuth App 10분만에 만들기

Introduction

OAuth 를 구조적으로 제공하는 passport.js 인증 미들웨어를 소개한다. passport 의 인증API 는 알고나면 간단한데, 처음에 Wall 이 있다. 이를 Node.js 입문자에게 꼭 해결해주고 싶어 이 글을 작성한다.

이 글은, Twitter, Facebook, Google 총 3편으로 연재할 것이다.

oauth<em>twitter</em>logo.png

이번편을 OAuth+App을 10분안에 제작하는 것에 목표를 둔다. 목표를 달성하면, Facebook, google 은 5분만에 달성하게 될 것이다.

Skeleton

최근 내가 제작한 nodestatic 을 사용한다. 이것은 최소의 코드로 원하는 웹서버를 관리할 수 있다.

한국 개발자들은

그리하여, 최소한의 웹서버만 관리해주는 것을 제작하였다.

$ curl s.nodeqa.com | bash

Installation

passport, passport-twitter 2개의 모듈을 설치한다.

$ npm install passport --save
$ npm install passport-twitter --save

그리고, login session 변수를 html 에서 구분할 수 있도록 ejs template module 을 설치한다.

$ npm install ejs --save

--save 옵션은 package.json dependencies property 에 자동추가되는 역할을 한다.

디렉토리/파일 구성내용이다.

./passport_project/
  404.html
  init*
  package.json
  static-app.js
  node_modules
    passport
    passport-twitter

package.json 파일을 확인하자.

{
  ...
  ...
  "dependencies": {
    "express": "*",
    "ejs": "*",
    "passport": "*",
    "passport-twitter": "*"
  },
  ...
}

Twitter App Create

OAuth 를 사용하기 위해서는 각 서비스에 App 을 등록해야 한다.

twitter dev center 에 로그인하여, App 을 등록하자.

그리고, Consumer key / secret 2개의 정보를 복사하여, package.json 파일에 등록하고, 이를 나중에 사용하자.

callbackURL 은 중요하니 아래 내용 중 your_host 부분만 수정하자.

{
  ...
  "oauth": {
    "twitter": {
      "TWITTER_CONSUMER_KEY": "yzTvZFr.......ykrqQA",
      "TWITTER_CONSUMER_SECRET": "spAgPbBELJk7................HbQc9LbVlMkQ",
      "callbackURL": "http://your_host/auth/twitter/callback"
    }
  },
  ...
}

Passport

passport 관련 작업을 시작하자

static-app.js

main 파일이다.

/**
 * Static simple web server
 * Only use Express module
 *
 * @author nanhaprk <nanhap@gmail.com>
 * @homepage http://nodeqa.com
 */
//
// Usage
//
if (process.argv[2] == '-h') return console.log('Usage: node ./static-app.js [port] [process_title]\\ n');
//
// ps on linux
//
process.title = process.argv[3] || 's.nodeqa.com : Static WebServer';
//
// declare
//
var express = require('express'),
    fs = require('fs');
var port = parseInt(process.argv[2], 10) || 8080;
var app = express();

// --------
// !! 아래 부분을 추가하자. ejs 와 session
//
app.set('views', __dirname);
app.set('view engine', 'ejs');
app.use(express.cookieParser());
app.use(express.session({
  key    : 'sid',
  secret : 'secret'
}));
// --------

//
// static serving
//
app.use(express.static(__dirname));

//
// **oauth.js** 파일에 OAuth 관련 설정을 하자. app 객체를 전달하자.
//
require('./oauth')(app);

app.get('/', function(req, res) {
  //
  // 세션정보를 확인한다.
  //
  console.log(req.session);
  //
  // req.user 는 아래에서 설명한다.
  // 처음에 undefined 이나, 로그인 성공하면, profile 정보가 저장된다.
  //
  console.log(req.user);

  res.render('index', { user: req.session.passport.user || {} });
});
//
// 404 page
//
app.get('*', function(req, res) {
  res.type('html').send(404, fs.readFileSync('404.html'));
});
//
// listen
//
app.listen(port, function() {
  console.log("\x1B[36mStart static server, listen port: " + port + '\x1B[39m')
});

oauth.js

module.exports = init;

function init(app) {

  var pkginfo = require('./package');
  var passport = require('passport');

  app.use(passport.initialize());
  app.use(passport.session());

TwitterStrategy 를 추가한다.

  var TwitterStrategy = require('passport-twitter').Strategy;

Twitter 에 정보를 전달하고 받을 경우에 사용되어진다.

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

Comsumer 정보를 설정한다.

  passport.use(new TwitterStrategy({
    consumerKey: pkginfo.oauth.twitter.TWITTER_CONSUMER_KEY,
    consumerSecret: pkginfo.oauth.twitter.TWITTER_CONSUMER_SECRET,
    callbackURL: pkginfo.oauth.twitter.callbackURL
  }, function(token, tokenSecret, profile, done) {
    //
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // req.session.passport 정보를 저장하는 단계이다.
    // done 메소드에 전달된 정보가 세션에 저장된다.
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //
    return done(null, profile);
  }));

위에서 profile 정보는 Http Client Request (req) 객체의 user property 에 자동 할당된다.

기본적인 twitter Strategy 의 route 이다.

...

  app.get('/auth/twitter', passport.authenticate('twitter'));
  //
  // redirect 실패/성공의 주소를 기입한다.
  //
  app.get('/auth/twitter/callback', passport.authenticate('twitter', {
    successRedirect: '/',
    failureRedirect: '/'
  }));
  app.get('/logout', function(req, res){
    //
    // passport 에서 지원하는 logout 메소드이다.
    // req.session.passport 의 정보를 삭제한다.
    //
    req.logout();
    res.redirect('/');
  });
}

index.ejs

메인 HTML 파일이다.

<a href="/auth/twitter">twitter login</a>

<% if (user.username) { %>
  <a href="/logout">logout</a>
  <%= user.username %>
<% } %>

실행하기

start

$ ./init start 8082 passport_app
Starting static webserver: PID: 4252

stop

$ ./init stop
Stopping static webserver: PID: 4252

open

$ open localhost:8082

세션확인

login 버튼을 클릭하고, req.session 을 확인하면, 아래와 같은 정보를 확인할 수 있다.

{ cookie:
   { path: '/',
     _expires: null,
     originalMaxAge: null,
     httpOnly: true },
passport:
   { user:
      { provider: 'twitter',
        id: 41317247,
        username: 'nanhapark',
        displayName: '나나하치',
        photos: [Object],

/logout 을 수행하면 session 정보는 아래와 같아진다.

{ cookie:
   { path: '/',
     _expires: null,
     originalMaxAge: null,
     httpOnly: true },
passport: {} }jjj

혹은 로그인 확인하는 다른 방법은 req.isAuthenticated() 메소드를 사용하는 것이다. 반환값은 true/false 이다.

Conclusion

이제 누구나 OAuth 인증을 도입할 수 있게 될 것이다. 다음편에는 Facebook 편을 연재하겠다.

추천 관련글