認証ダイアログの移行

新しい認証ダイアログが導入されて以来、ユーザから見た認証ダイアログのステップは以下の2つに別れました。
  1. ユーザ本人とその友だちの情報に関するパーミッションを要求するステップ。
    ここで要求されるパーミッション全てを許可しない限り、ユーザはアプリをインストールすることができません。
  2. 追加のパーミッションを要求するステップ。
    ユーザはどのパーミッションを許可するか選択することができます。

以前は、アプリをインストールするには要求される全てのパーミッションを許可する必要があり、与えたパーミッションを消すには、導線の分かりづらい設定ページへ行くしかありませんでした。しかし、新しい認証ダイアログ移行後は、認証ステップの途中で与えるパーミッションを取捨選択できてしまいます。アプリ側はこれまで以上に気をつけて、どのパーミッションが与えられているのか把握する必要があります。

許可されたパーミッションを確認する

タイミング

では、いつ確認するかという問題ですが、新規ユーザがアプリをインストールしたときと既存ユーザがログインするときの2つのタイミングが良いのかなと思っています。既存ユーザのログイン時にも都度チェックするのは、インストール時に許可を与えた後でも、プライバシ設定で任意のパーミッションを取り消すことができる為です。

利用するAPI

ユーザが許可したパーミッション一覧を得るには、以下のいずれかのエンドポイントへアクセスします。
  • https://graph.facebook.com/USER_ID/permissions?access_token=APP_ACCESS_TOKEN
  • https://graph.facebook.com/me/permissions?access_token=USER_ACCESS_TOKEN
これにより、以下のようにユーザが許可したパーミッションの一覧を得ることができます。
'data' => [
    {
        'installed' => 1,
        'publish_actions' => 1
    }
]

バッチリクエストを使う

流れ

ログイン時にチェックしようとすると、取得したアクセストークンからユーザ情報を取得し、許可されたパーミッション一覧も取得するという2度のリクエストを行うことになってしまい、レスポンスが遅くなってしまいます。
こういった場合にはBatch Requestで複数リクエストを1つにまとめてしまうのが便利です。このAPIの仕様はBatch Requestの和訳を参照してください。
my $batch = encode_json([
    +{ method => 'GET', relative_url => 'me', },
    +{ method => 'GET', relative_url => 'me/permissions', },
]);

my $query = +{
batch => $batch,
access_token => $access_token,
};

my $ua = LWP::UserAgent->new;
my $response = $ua->post(
"https://graph.facebook.com/", $query
); my $datam = decode_json( $response->content );
これにより、以下のような形式でデータを取得できます。
$VAR1 = [
          { # 1つ目のリクエストのレスポンス
            'body' => '{ ....... }', # ユーザ情報
            'headers' => [ ..... ], # HTTPヘッダ情報
            'code' => 200 # status code
          },
          { # 2つ目のリクエストのレスポンス
            'body' => '{"data":[{"installed":1,"publish_actions":1}]}', # パーミッション一覧
            'headers' => [ ..... ], # HTTPヘッダ情報
            'code' => 200 # status code
          }
];

Facebook::Graphで

Facebook::Graphでバッチリクエストを利用するため、Facebook::Graphを継承するMyApp::FacebookGraphに以下のようにbulk_fetchとbatchメソッドを足して使っています。
package MyApp::FacebookGraph;
use Any::Moose;
extends 'Facebook::Graph';
.........

sub bulk_fetch {
my ( $self, $paths ) = @_;
my @queries = map {
+{ method => 'GET', relative_url => $_ }
} @$paths;

return $self->batch( \@queries );
}
sub batch { my ($self, $batch) = @_;

my $query = +{ batch => encode_json( $batch ), access_token => $self->access_token, };

my $bulk_res = $self->ua->post( $self->uri->as_string, $query ); my @datam = (); my $contents = Facebook::Graph::Response->new( response => $bulk_res )->as_hashref; for my $content ( @$contents ) { my @headers = map { $_->{name} => $_->{value} } @{$content->{headers}}; my $http_res = HTTP::Response->new( $content->{code}, undef, \@headers, $content->{body} ); my $fb_res = Facebook::Graph::Response->new( response => $http_res ); push @datam, $fb_res->as_hashref; } return \@datam; }

Batch RequestではHTTPヘッダ情報を含むレスポンスが配列で返されるので、batchメソッドではそれを元にHTTP::Responseオブジェクトを生成し、Facebook::Graph::Responseに渡しています。それをas_hashrefしたものを配列に入れて返しているのですが、これにより、配列の個々の要素はfetchメソッドの返り値と同等になっています。また、Facebook::Graph::Responseオブジェクトのas_hashrefがステータスコード確認と例外を投げるところまでやってくれるので、それを流用することで既存のfetchメソッドと同様に例外など扱えるよう意識し、置き換え易いようにしています。


先ほどのようにユーザ情報とパーミッション一覧を得る場合だと、以下のように書けます。

my $fb = MyApp::FacebookGraph->new( .... );
$fb->access_token( $access_token );
my $datam = $fb->bulk_fetch(['me', 'me/permissions']);

#my $datam = $fb->batch([ # +{ method => 'GET', relative_url => 'me', }, # +{ method => 'GET', relative_url => 'me/permissions', }, #]);

返り値は以下の通りです。

$VAR1 = [
          {
            'link' => 'http://www.facebook.com/profile.php?id=100003754895087',
            'timezone' => 9,
            'middle_name' => 'Amcgedhiejhg',
            'name' => 'Sharon Amcgedhiejhg Schrockberg',
            'locale' => 'ja_JP',
            'last_name' => 'Schrockberg',
            'updated_time' => '2012-04-11T02:22:58+0000',
            'id' => '100003754895087',
            'first_name' => 'Sharon',
            'gender' => 'female'
          },
          {
            'data' => [
                        {
                          'installed' => 1,
                          'publish_actions' => 1
                        }
                      ]
          }
        ];

名前がSharon Schrockbergになっているのは、アプリ管理ページで作成したテストユーザだからです。実際の自分のアカウントを使っていると、APIのテストで連続投稿したときにスパム判定されてしまったりするので、テストユーザを使うのが推奨されています。詳細についてはTest Usersの和訳を参照してください。