Node.js
async
FacebookGraphAPI
request

iPhoneでFacebook認証したTokenをNode.jsのサーバで受けてサーバ側で認証してみる

More than 3 years have passed since last update.

はじめに(ごめんなさい的な)

自分なりにいろいろ調べてやってるんですが、もっと効率的な方法はある気がしてます。
そもそも間違っているところもあるかもしれないので、気づかれた方は是非ツッコミをお願いします。

やりたいことのイメージ

やりたいことはこれ。

  • iPhoneアプリをFacebook認証を使って使用させたい。
  • Facebook認証の後、アプリ操作でFacebook友達情報を取得したり、その友達とのつながりを使った機能を提供したい。

ちょっと図示してみました。
この記事でのポイントは、下記のです。
Facebook認証の話なんかは他にもいっぱいあるので、特に触れません。
(ただ、やってるときにFacebook SDKのバージョンが上がっちゃって面倒だったけど…)

figure.png

①iPhoneアプリ上で、Facebook SDKを使ってAuth Requestを送信
②FacebookからAccess Token(図中はAuthToken)を受け取る
③取得したAccess Token(図中はAuthToken)をサーバ(Node.js)に送信
④受け取ったAccess Tokenを使って、iPhoneユーザの情報を取得するようFacebook graphにリクエストする
⑤Facebookからユーザ情報を受け取る

AccessTokenは同一アプリの範疇なら使い回しOKなので、この方法で認証してみたいと考えました。

サーバ側の実装で困っていたこと

これはただ自分が無知ゆえに困っちゃってたんですが。。

Node.jsはノンブロッキング処理でポンポン進んじゃうわけで、
④のFacebook Graph APIにアクセスしたときに、⑤が返ってくる前に先に進んでしまう。
Callback関数の使い方とか、いまいちちゃんとわかっていない証拠ですな。

試行錯誤して次のような実装にしました。

サーバ(Node.js側)の実装

asyncモジュールのseries、といっても1個だけなんですが、
⑤が返ってくるまで待つようにしたつもりです。実際、下記でちゃんと取れています。

なお、Facebook Graph APIのアクセスにはHTTPアクセスをするのにいろんな方法があるっぽいが、
requestモジュールを使いました。
(返って深くなっちゃう理由になった気もしますが。。。)

var async = require('async');

var accessToken = "FACEBOOKのACCESS_TOKEN";

async.series([
    function(callback) {
        var qs = require('querystring');
        var request = require('request').defaults({strictSSL:true});

        debug('ACCESS_TOKEN: ' + accessToken);

        var apiUrl = 'https://graph.facebook.com/v2.3/me?' +
            qs.stringify({ access_token: accessToken });

        var options = { url: apiUrl, json: true };

        request.get(options, function(err, resp, data) {
            if(err) {
                // (1) Error handling
                debug('ERROR');
                callback(err, null);
            } else if(resp.statusCode !== 200) {
                // (2) Unsuccessful handling
                debug('UNSUCCESSFUL');
                callback(new Error('Unsuccessful'), null);
            } else {
                // (3) SUCCESSFUL
                debug('SUCCESSFUL');
                callback(null, data);
            }
        });
    }
],function(err, results) {
    if(err) {
        //Case in (1) and (2)

        //[A] ERROR PROCESS
    } else {
       //Case in (3)

       //[B] SUCCESSFUL PROCESS
    }
});

(1)はrequest.get()処理のなかでエラーが起きた状態。
(2)はちゃんと答えが返ってきたけど、ResponseCode:200以外なので、APIから期待する結果が返ってこない状態。
(3)は期待通りの答えが得られている状態。

(1),(2)はエラーの類として[A]で処理をします。
(3)のみが正常系として[B]で処理されます。

そんな流れです。

課題というか、理解不足

要はAPIからResponseが返ってきたら次にいくような同期処理だったらいいわけなんですが、
requestモジュールを使うと、すでに非同期で取ってくるので全体として非同期になってしまいます。

本来Nodeのいいところは、イベント駆動・ノンブロッキング処理ができるところだという理解なんですが、
これをうまく制御できないと、いいところがうまく生かせないもんね。

もっとちゃんと勉強していきたいと思います。

※自身の理解度確認も含めてこの記事を書いています。しつこいようですが、謝りに気づかれた方は是非ご指摘いただけると幸いです。