passportのtwitterOauthをsocket.ioに渡す(Express4)

## 動作環境
* Node.js 0.10.36
* Express 4.12.3
* Passport 0.2.1
* Socket.io 1.3.5
* OSX 10.10.2

で確認した。

## 事前準備
[TwitterApps][1]で、認証に使用するURLを登録する。注意事項として
* Setting> Callback URLの入力
* Setting> ✔ Allow this application to be used to Sign in with Twitter
上記を忘れるとPINの入力を求められる。

# やること
1. PassportでtwitterOauthする
2. PassportセッションをSocket.ioに渡す
3. ブラウザにPassportセッションをemitする（handshakeの確認）
4. 上記をなるべく少ないパッケージで

ということで、以下のライブラリで解決した。

```json:package.json
{
  "dependencies": {
    "cookie-parser": "^1.3.4",
    "express": "^4.12.3",
    "express-session": "^1.10.4",
    "passport": "^0.2.1",
    "passport-twitter": "^1.0.3",
    "socket.io": "^1.3.5"
  }
}
```

package.jsonを使わない場合は

```bash
$ npm install cookie-parser express express-session passport passport-twitter socket.io
```

とします。

<del>
`git clone`して`cd`して`npm start`するだけで[動くサンプルを用意した][2]ので、忙しい人やCoffeeScriptスキーの人はそちらを参照して欲しい。そちらのほうが見易い
</del>

```bash
$ node app.js
```

```js:app.js
// Generated by CoffeeScript 1.9.1
var TwitterStrategy, app, base, cookie, express, expressSession, expressSessionStore, http, io, parser, passport, passportConfig, server, socketio;

if ((base = process.env).PORT == null) {
  base.PORT = 59798;
}

// dependencies

express = require('express');
expressSession = require('express-session');
expressSessionStore = new expressSession.MemoryStore;

passport = require('passport');
passportConfig = {
  twitter: {
    consumerKey: 'https://apps.twitter.com/で取得する',
    consumerSecret: 'https://apps.twitter.com/で取得する',
    callbackURL: "http://localhost:" + process.env.PORT + "/callback"
  }
};
TwitterStrategy = require('passport-twitter').Strategy;

http = require('http');
socketio = require('socket.io');
cookie = require('cookie-parser/node_modules/cookie');
parser = require('cookie-parser');

// passport setup

passport.serializeUser(function(user, done) {
  return done(null, user);
});
passport.deserializeUser(function(obj, done) {
  return done(null, obj);
});
passport.use(new TwitterStrategy(passportConfig.twitter, function(token, tokenSecret, profile, done) {
  return process.nextTick(function() {
    return done(null, profile);
  });
}));

// express setup

app = express();
app.use(expressSession({
  store: expressSessionStore,
  secret: 'keyboard cat',
  resave: true,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());
app.get('/', function(req, res) {
  var html, ref;
  html = "<script src=\"/socket.io/socket.io.js\"></script>\n<script>io.connect().on('authorized',function(session){document.querySelector('pre').innerHTML=session})</script>\n<h1>Passport for Socket.io1.3</h1>\n<pre></pre>";
  if (((ref = req.session.passport) != null ? ref.user : void 0) == null) {
    html += '<a href="/auth">twitterOauth</a>';
  }
  return res.end(html);
});

app.get('/auth', passport.authenticate('twitter'));
app.get('/callback', passport.authenticate('twitter', {
  successRedirect: '/',
  failureRedirect: '/failure'
}));
app.get('/failure', function(req, res) {
  return res.end('だめでした');
});

// socket.io and http-server setup

server = http.Server(app);
server.listen(process.env.PORT);
io = socketio(server);
io.use(function(client, next) {
  var cookies, sid, signedCookies;
  signedCookies = cookie.parse(client.request.headers.cookie);
  cookies = parser.signedCookies(signedCookies, 'keyboard cat');
  sid = cookies['connect.sid'];
  return expressSessionStore.get(sid, function(error, session) {
    if (error != null) {
      return next(error);
    }
    if (session.passport.user == null) {
      return next('Guest');
    }
    client.session = session;
    return next();
  });
});
io.on('connect', function(client) {
  return client.emit('authorized', JSON.stringify(client.session, null, 2));
});
```

`passportConfig.twitter.consumerKey`と`passportConfig.twitter.consumerSecret`は、お手持ちの文字列に置き換えて実行してください。<del>（ローカルで確認するだけならそのままでも動く）</del>

* / にアクセスする（ http://localhost:59798 ）と、ログインの可否を表示する。
* /auth にアクセスすると、twitterOauthを行う。
* /callbackでPassportが成否を判断し、成功時はTwitterStrategyの第二引数を経由して、セッションに書き込む
* 成功なら / に戻り、socket.ioサーバーからセッションを受け取り、その内容を&lt;pre&gt;内にレンダリングする。
* 失敗なら /failureに飛び、「だめでした」となる

以上です。<del>、楽しい沼ライフを</del>

# 参考
* [Socket.IO と Express でセッションの共有 - Block Rockin’ Codes - Jxck][7]
* [User Authentication with Passport and Express 4 - Michael Herman][6]
* [Node.js + Express + passport-twitterで認証Webアプリをつくる - itagakishintaro][3]
* [Node.js + Express + passport でTwitterのタイムラインを読み込む - sckm][4]
* [express4でsocket.io 1.0xを使ってみる - daikon_buu][5]

[1]: https://apps.twitter.com/
[2]: https://github.com/59naga/socket.io-passport-example
[3]: http://qiita.com/itagakishintaro/items/e5a0481b51e6a17b304c
[4]: http://qiita.com/sckm/items/e6a7fd669e2367bc441f
[5]: http://qiita.com/daikon_buu/items/7d506baec9e5a17cba5d
[6]: http://mherman.org/blog/2015/01/31/local-authentication-with-passport-and-express-4/#.VQ-pfFWsUgU
[7]: http://jxck.hatenablog.com/entry/20110809/1312847290