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 でログインが完了しちゃってたからでした。