概要

Facebook Page 管理系のアプリを作っている方には既知の問題かもしれませんが、先日、拙作の Facebook::OpenGraph について croak 'expires is not returned' という issue が追加されました。

本来の仕様では、/oauth/access_token エンドポイントからアクセストークンを取得した場合、それは最大でも約 60  日間で無効になることになっています。 その際に一緒に返されるのが expires で、開発者はこの値を見ることでトークンの有効期限を知ることができます。ところが、一定の条件を満たした場合はアクセストークンの期限が無期限になり、expires が返されないことがわかりました。

Facebook::OpenGraph のアクセストークン取得系メソッドでは access_token と expires が正しく返されることをチェックしているため、issue にある通りエラーになっていたのです。Facebook Platform にバグ報告したところ、これは望んでいる挙動ではないものの、現段階の動作としては正しいとの回答がありました。

前提

昔は offline_access パーミッションを取得することで、長期間利用可能なアクセストークンを取得することが可能でした。当時は通常のアクセストークンが 2 時間程度で切れてしまっていたため、ある程度長時間使うことが想定されるアプリや、夜間のバッチ処理でユーザの情報を取得したいアプリであれば、offline_access パーミッションを取得するのが一般的でした。
そのパーミッションは数年前に廃止され、代わりに最大約 60 日有効なアクセストークンを取得する手段が提供されました。この時点から、ユーザのアクセストークンは必ず有限であり、access_token と expires がセットで返される仕様になっています。

問題

今回報告されたケースでは、以下の手順でアクセストークンが無期限になり、expires が返されなくなることが確認できました。
  1. scope=manage_pages を指定した Login Dialog にユーザをリダイレクトする
  2. ユーザは manage_pages パーミッションを許可し、redirect_uri で定めた URL へ着地する
  3. 与えられた code パラメータを /oauth/access_token へ投げ、アクセストークンを取得する(この時点では expires も返される)
  4. expires が短期間であった場合、/oauth/access_token で 60 日間有効なトークンを取得する
  5. /{user-id}/accounts にアクセスし、ユーザが管理する Facebook Page のアクセストークンを得る(この時点でユーザのアクセストークンは無期限になる)
  6. デバッグツールでアクセストークンの詳細を調べ、無期限になっていることを確認する
    851591_750115185018891_1908473441_n
  7. ユーザを再度 Login Dialog に飛ばす
  8. 与えられた code パラメータを /oauth/access_token へ投げ、アクセストークンを取得する(expires は返されない)
このような動作は仕様にないため、常に expires が返される前提でチェックしている Facebook::OpenGraph ではエラーになります。そこで、Facebook Platform に「/oauth/access_token のドキュメント不備もしくは API のバグ」というチケットを登録して正式な回答を待ちました。
一点補足ですが、通常、上述のステップ 3 では短期間有効なアクセストークンのみ与えられ、それをもとに /{user-id}/accounts で Facebook Page 用のトークンを取得すると短期間のトークンのみが返されます。無期限の Page アクセストークンを得るには、必ずステップ 4 で長期間有効なユーザアクセストークンを取得しておく必要があります。が、今回確認したケースでは、manage_pages を要求した場合はステップ 3 の段階でも 60 日有効なトークンが返されました。

Facebook からの回答

回答によると、Facebook Page のトークンとユーザを関連付ける現在の実装上、この動作は正しいそうです。ただし、本来意図している動作ではないために、ドキュメントには明記されていないとのことでした。現実装の課題がクリアされれば access_token の期限は常に有限になり、 expires も返されるようになりそうな雰囲気です。また、Ads API を利用するアプリでも同様の挙動をするそうです。(回答中では、「expires が空文字列になったり 0 になる」と表現されていましたが、確認した限りでは expires というキー自体渡されません。)
access_token を無期限で使えるようにする裏ワザにもなりそうですが、回答の最後に「この目的のためだけに manage_pages を要求するのは認められていない」と念を押されました。4/30 の f8 以降、追加でパーミッションを要求する場合には Facebook Platform によるレビューを通らなければならないため、不必要な manage_pages を要求した場合はその段階で却下されるだろうと思います。

おまけ

ちょっと前から、Bug 詳細ページでユーザ名をマウスオーバーすると、そのユーザの Bug Karma や報告したバグ一覧、購読中のバグ一覧が表示されるようになりました。似たアプリを作っている開発者の報告したバグ等が探しやすくなりそうで良いですね。
が、下のキャプチャにある通り、一覧を表示しようとするとエラーになってしまいます。
10173493_777958702238030_524643995_n
というわけでこれもバグ報告したところ、Assigned になりました。バグ報告画面のバグ報告ってなんかシュール…