facebook-node-sdk の初期化より先に session を有効化しないといけない

node.js で Facebook API 使うときに facebook-node-sdk つかってるんだけど、 loginRequired() の middleware つかったときに、アプリ新規承認時に redirect_uri に無限ループするということがあったりした。

で、出てたログが

DEBUG: DEBUG: CSRF state token does not match one provided.

で、redirect 前に設定してた CSRF Token が一致してないとのこと。
で、これがどこででてるかというと、

BaseFacebook.prototype.getCode = function() {
  var code = this.getRequestParam('code');
  if (code !== null) {
    var state = this.getRequestParam('state');
    if (this.state !== null && state !== null && this.state === state) {

      // CSRF state has done its job, so clear it
      this.state = null;
      this.clearPersistentData('state');
      return code;
    } else {
      this.errorLog('CSRF state token does not match one provided.');
      return false;
    }    
  }
  return false;
};

ここで、つまり、state がないか一致しないときに出てる。考えられる原因は、redirect 前にセットしてる state が set できてないか、戻ってきた時に違う state が入ってる or セットしたはずの state がとれてない、とかそういうかんじだとおもうんだけど、このときの state と this.state がどうなってるかというと (console.log !!)、

this.state: null
state: 5GluXPtLoAzcvSYMJ1JU6Mm52J5DKd2u

つまり、

BaseFacebook.prototype.establishCSRFTokenState = function() {
  if (this.state === null) {
    var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var buf = [];
    for (var i = 0; i < 32; i++) {
      buf.push(chars[Math.floor(chars.length * Math.random())]);
    }
    this.state = buf.join('');
    this.setPersistentData('state', this.state);
  }
};

でセットされてるはずの state が無い。このロジック自体はあまりおかしくないかんじがする。 setPersistentData はというと、

Facebook.prototype.setPersistentData = function(key, value) {
  if (this.hasSession) {
    this.request.session[key] = value;
  }
};

ということになってて、この hasSession が false になってた。あれ、session つかってるだけど、

function Facebook(config) {
  this.hasSession = !!(config.request && config.request.session);
  BaseFacebook.apply(this, arguments);
}

で、中身をみてみると、 config.request はあるけど、 config.request.session がない。

app.js を見てみると、

   app.use(facebook.middleware({appId: config.facebook.appId, secret: config.facebook.secret}));
   app.use(express.session({ secret: config.session.secret }));

あー、ってことで、

   app.use(express.session({ secret: config.session.secret }));
   app.use(facebook.middleware({appId: config.facebook.appId, secret: config.facebook.secret}));

これで、治った。


ちなみに、なんでアプリ新規承認時のみ発生していたかというと、承認済みの場合、JavaScript SDK のほうの cookie でログインが完了しちゃってたからでした。

YAML で UNIQUE INDEX

uniqueConstraints をつかう。

Crocos\HogeBundle\Entity\Fuga:
  repositoryClass: Crocos\HogeBundle\Entity\FugaRepository
  type: entity
  table: hoge_fuga
  fields:
    hoge_id:
      id: true
      type: integer
    hoge_key:
      type: string
      length: 255
    value:
      type: text
  uniqueConstraints:
    hoge_id_hoge_key:
      columns: [ hoge_id, hoge_key ]

ドキュメントは Annotation と XML には詳しく書いてあるけど YAML がザルである。

heroku に redmine インストール

node.js から redmine 叩くものをつくっていたけど api のテスト用 redmine が欲しいと思って自分のサーバだと面倒だから heroku でも使ってみるか、ということでさっそくやってみた。

ほとんど以下リンク通りだったのでメモのみ。


Redmine 1.3 だったので、1.3-stable ブランチから派生した 1.3.0-for-heroku ブランチで作業。 vendor/gems/coderay-1.0.0 が 1.0.0 になってたのと、インストールする rails がのバージョンが違ったくらいで、ほぼ上記の通りでいけた。

で、heroku に push するだけ。migration はちょっと時間かかったな。スペックの問題かしら。

$ git push heroku +1.3.0-for-heroku:master


で、 node-redmine も issues だけ対応できた。テスト書かこう。(そのための redmine ...
facebook-node-sdk のマネして env から redmine の host と apikey 渡すようにして travis.ci でやってみたいと思っているのでそれでテストを書いてみよう。

OAuthException: An active access token must be used to query information about the current user

Facebook PHP SDK の話。

/me とかを user の access token じゃなくて アプリの access token とかで叩いちゃうと出るんだけど、そもそもその前に getUser() で user_id が 0 じゃないことを確認してから叩いても出ることがあったので調査してた。

  • signedRequest から getUser する → user_id だけ persistentData に入る
  • その後 API を一度も呼び出さないと accessToken は persistentData (session) に入らない
  • 次のページで、signedRequest が無い (つまり、GETで普通にページ遷移)、かつ、session から user_id がとれた、かつ、session から accessToken はとれない、かつ、cookie からもとれない(JS SDK 経由のやつ)場合、userAccessToken がとれない
  • こうなると getUser() のゼロチェックはすりぬけて /me を叩いてしまう

(いやそもそも↑の状況は結構発生させにくいんだけどたまにあるのよね)

Signed Request からデータ取得したときに User Access Token を session に入れればいいと思うんだけどなんでそうなってないんだっけ?>SDK

とりあえず override しちゃえとおもったけど Facebook::getUserAccessToken() は protected だったので hasUserAccessToken() をつくってチェックできるようにした。

express.cookieParser()

session つかうときはこれもいっしょにしないとこれが出る。

TypeError: Cannot read property 'connect.sid' of undefined

app.jsとかで。

app.configure(function(){
  app.use(express.cookieParser());
  ..
  app.use(express.session({ secret: config.session.secret }));
}

php 5.4 debian package memo

わかっているダメな点

  • php5-sqlitesqlite.ini と sqlite3.ini が作られてしまう。sqlite.so は作られないのに
  • fpm の config がおかしい (修正済み

debug build

  • symbol がやっぱりけされちゃう dh_split しないようにしたんだけど、というか、一応ビルド後のバイナリを見ると symbol あるように思えるんだけどなんでだっけ...
  • fpm の debug 方法がわからない... thread だから?

TODO

  • apache も自前でパッケージングする?
  • nginx はどうしましょう
  • xdebug は、する

cron.d にファイル置く場合の注意点

root 権限じゃないとダメ (BAD OWNER) なのは有名な話だけど、permission も 644 じゃないと (というか、owner 以外に w がついてると)だめだった

Sep 12 06:00:02 localhost /usr/sbin/cron[8974]: (*system*xxxxx) WRONG INODE INFO (/etc/cron.d/xxxxx)

symlink で管理してる場合は、ファイル本体の permission と owner を root, 644 にしないとですね。