Androidはワンツーパンチ 三歩進んで二歩下がる

プログラミングやどうでもいい話

AndroidアプリでFacebookのパーミッションを取得した話

このポストではFacebook API ver2.0 / Graph API v2.4の時点の情報を書いています。


FacebookのGraph APIを利用したAndroidアプリを作りました。
そのアプリはデモアプリみたいなもので、画面のデザインや素材作成も含めて製作期間は一週間ぐらいの小さなものです。
その時にFacebookに権限を申請したので経緯をまとめておきます。

まずは参考にさせていただいたサイト様にお礼を。

snowadays.jp

こちらのサイト様はこのページ以外もFacebook関係の記事が豊富でとても助かりました。
本当にありがとうございます。おかげでなんとか審査に通過いたしました。


Facebookは2014年4月にAPI2.0にバージョンアップする際に大幅に仕様変更しています。
中でも大きな違いは、public_profile、email、user_friends以外の権限を要求するアプリはFacebookに審査を申し込んで取得しなければいけなくなりました。

developers.facebook.com

snowadays.jp


私が申請したパーミッションはuser_postsとuser_likesでした。
これはユーザーがポストした内容を取得する権限とユーザーがいいね!した項目を取得する権限です。
各権限の情報は公式サイトのこちらに記載されています。

developers.facebook.com


アプリ準備

FacebookSDKを使ったり、権限を必要とする処理のあるアプリを作成する場合、Facebookデベロッパーサイトからアプリを登録する必要があります。アプリ登録については手前味噌で恐縮ですが、こちらのエントリーを見ていただければと思います。

sakura-bird1.hatenablog.com

アプリを作る

ログイン

FacebookSDKを導入して、アプリを作っていきます。
アプリでFacebookのGraph APIを使用する場合、たいていはアクセストークンを必要とします。
アクセストークンはいくつか種類がありますが、多くの場合ユーザーアクセストークンを必要とします。
ユーザーアクセストークンはユーザーにログイン画面からログインしてもらい、Facebookに認証されることで取得できます。
ログインの際に必要なパーミッションを要求することで、そのアプリが必要とする権限を有したアクセストークンが取得できます。

developers.facebook.com


Androidのログイン部分について簡単にまとめた記事があります。
LoginManagerのlogInWithReadPermissionsメソッドを使用してパーミッションを列挙します。

sakura-bird1.hatenablog.com

権限のテスト

ここで、まだFacebookパーミッションを申請もしていないのにどうやってパーミッションが必要な処理のテストをすればよいかという疑問が起こると思います。

参考サイト様のこちらのエントリーにて詳しく説明してくださってますのでご覧ください。
申請の時にレビュアーに使ってもらうテストユーザーを作成するとよいと思います。

snowadays.jp


アプリ完成

いきなりですがアプリが完成したとします。
(このエントリーはパーミッション取得にフォーカスしていますので途中の紆余曲折を端折っております。)
が、実は完成とまで行かないけどFacebookの権限使用部分は再現できるという段階でも申請可能なようです。
私のアプリもプロダクトレベルのアプリにするにはまだまだ作りこむべき点が多い状態でしたが、申請して承認されました。
あくまでも私の体験ですので、確実ではないですよー。

パーミッション申請前に、これらのことに注意してください。

・現在のアプリで必要としている以上の権限を申請しないこと
パーミッションを使用することでユーザーエクスペリエンスが改善されること
Facebook Platform Policyを遵守していること
・ログインのベストプラクティスに従っていること
(https://developers.facebook.com/docs/facebook-login/best-practices)


パーミッション申請準備

App Detailsを埋める

いよいよパーミッションを申請するわけですが、前述のこちらのサイト様にあるように必要事項を埋めていきます。
デベロッパーサイトのタイトルバーのMy Apps > アプリ > App Derailsあたりです。

snowadays.jp

私のアプリはこの画像のように指定しました。こんなんじゃだめかなーと思いましたが大丈夫だったのでお恥ずかしいですが、載せて起きます。

f:id:sakura_bird1:20151003162827p:plain

f:id:sakura_bird1:20151003162843p:plain

テストユーザーの準備

上でも少し触れましたが、レビュアーがパーミッションを使った機能を再現出来るようにテストユーザーを用意しておくとよいと思います。
レビュアーは実際にログインや投稿などもしますので、安全のためにもテストユーザーを使ってもらうようにするといいでしょう。

私はuser_postsとuser_likesを使ったデータを表示するため、テストユーザーで何件か投稿し、ページを作成していいね!しました。
さらに投稿部分といいね!部分のスクリーンショットも用意しておきました。


qiita.com

スクリーンショットの準備

アプリの動きがわかるようスクリーンショットを用意しておきます。最低4枚です。
私はログインダイアログ、ユーザーが許可する画面、申請するパーミッションを使ったデータが表示されている画面、テストユーザーのfeedといいね!の画面(Web) のスクリーンショットを用意しました。
他にもアプリの機能はあるのですが、パーミッションに関係ない部分だったのでスクリーンショットを付けなかったのですが問題なかったみたいです。

2回ほど申請をリジェクトされたので、念を入れてスクリーンショットには番号と説明文を赤字で入れておきました。
一例を挙げておきます。英語がよくわからなくてきっと変です(;´Д`)


パーミッション申請

公式サイトはこちらです。developers.facebook.com

なお、申請は英語で記述します。私は全然自信がないのですがレビュアーさんも慣れているのか?下手くそでも大丈夫でした。

申請前の確認事項

申請の前に、できるだけスムーズに処理を行えるよう次の点を確認しましょう。

・App Detailsの"App Icon","Long Description","Privacy Policy"欄を埋めておきます。
パーミッションのテスト

1.Facebookのログインダイアログに必要な権限を表示しているか確認して下さい。
2.テスト可能な複数のユーザーでテストして下さい。テストユーザー同様、Developers, Admins, Testersを含みます。
3.Read Permissionsを使う場合、それぞれの権限を使用する内容にアプリがアクセス出来るようにして下さい。
4.Write Permissionsを使う場合、期待したようにポスト出来るようにして下さい。

 Rolesタブの中のユーザーはパーミッションのテストが出来ます。
 これらのアカウントを使用してもエラーが発生する場合、デバッグツールここをクリック)にアクセストークンをペーストし、次のことを確認します。
 1.App IDが申請する対象のものになっているか
 2.User IDはRolesタブ内に記述されているユーザーのものになっているか
 3.デバッグツールのScopesの欄に申請するパーミッションが入っているか

 これらを確認してまだエラーになる場合、
https://developers.facebook.com/bugsにアクセストークンとどのAPIリクエストを行おうとしたかを説明し、英語で報告して下さい。

申請を開始する

App Dashboard > Status & Review > 「Start a Submission」ボタンをクリックします。

レビュー対象のパーミッションを選択する

選択したら「Add (x) Items」ボタンをクリックします。

権限ごとに説明を記述する

「Add Notes」ボタンが表示されるのでそれぞれ説明を記述します。
パーミッションを使うことでどのようにユーザーエクスペリエンスが向上するか、アプリでどのようにパーミッションを使用する機能を使っているかを記述していきます。
これは公式サイトの画像です。

https://scontent.xx.fbcdn.net/hphotos-xtp1/t39.2178-6/11891338_1621998478087617_1216753649_n.png

私はこんな風に書きましたよ。
Notes for user_posts

Login with Facebook to the "XXXXXX App."
Accept the "user_posts" permission.
"XXXXX App." will display a list of the user's posts on the top page of left hand side
 of the tabs which use the "user_posts" permission.
 If user click  the one of posts, app shows Facebooks page of the post on the browser.
 And user can save each posts to the "favorites" so that user can access easily and faster.

申請フォームを完成させる

スクリーンショットをアップロードする
・APKをアップロードする
Facebook Login Integrationにアプリで権限を使用するフローを説明する

レビュアーが再現出来るよう手順を書きます。
ここでレビュアーに使って欲しいテストユーザーも明記しておきます。
私はこれをやらず「Test User(optional)」欄にテストユーザーを書いたから大丈夫だろうと思っていましたが、リジェクトされてしまいました。

私はこんな風に書きましたよ。
Facebook Login Integration

Hi, reviewer! Please examin my submission.

1. Launch the app.
2. The app shows the dialog. It says that this app needs to login to facebook.
3. Click OK.
4. Login screen will appear from Facebook SDK.
5. Please log in with this account.
   id : xxxxx_rosenthalsky_1442809748@tfbnw.net
   password : xxxxxx
6. Agree to the permission.
7. "XXXXX App." will get user's recent posts and likes that will appear on the two tabs.(I attached the screenshots No.5 and No.6)
8. On the evidence, you can see the screenshots from No.7 to No.8 that is the test users page you logged in.


Test User
name:Jennifer Alakchgjibbi Rosenthalsky
id:1524507284xxxxx
mail:xxxxx_rosenthalsky_1442809748@tfbnw.net
password:xxxxxx

thanks.

申請実行

「Submit for Review」ボタンをクリックして申請します。
7営業日かかるかもとありますが、2日ぐらいで結果がきました。シルバーウィークでも結果がきたので日本の祝日でもやってくれるみたいですね。まあそうですよね。

感想はめちゃ大変だったです。
このエントリーがどなたかの稼働時間を減らす助けになれればと思います。

以上です。

FacebookのAndroidアプリを作る。開発者登録〜FacebookアプリID取得

developers.facebook.com


AndroidFacebook SDKを使ったアプリを作成する場合、Facebookデベロッパーサイトでアプリを登録する必要があります。
その手順をまとめたいと思います。

(このエントリー作成時点のFacebook APIバージョンは2.0、Graph APIバージョンは2.4です。)

公式サイトもご覧ください。

1. Facebook開発者登録をする

Facebook開発者登録が済んでいない場合登録する必要があります。

https://developers.facebook.com/docs/apps/register
ここにアクセスします。

Create Developer Accountをクリックします。

プライバシーポリシーのところを「はい」にして、「登録」をクリックします。

f:id:sakura_bird1:20151003133651p:plain

完了ボタンをクリックします。

f:id:sakura_bird1:20151003133838p:plain

2.アプリを登録する

2-1.

Facebook開発者 - 開発者向けFacebook
ここにアクセスします。

タイトルバーから「New Apps」> 「Add a New App」を選択します。

すると次の画面が表示されますので、Androidを選択します。

1.で開発者登録をした場合は完了ボタンを押すと、この画面が表示されます。

f:id:sakura_bird1:20151003135000p:plain

入力欄が表示されますので、作成するアプリの名前を入力します。

f:id:sakura_bird1:20151003135258p:plain

「Create New Facebook App ID」ボタンをクリックします。

カテゴリ選択のダイアログが表示されますのでアプリのカテゴリを選択し、「Create App ID」ボタンをクリックします。

2-2.

SDKのダウンロードやエミュレータインストール用のAPKのダウンロードの案内が表示されます。
が、Android Studioを利用している場合、SDKをダウンロードする必要はないのでこちらからAndroid Studioにセットアップする方法を見た方がいいと思います。

developers.facebook.com


話は脱線しますが、Eclipseで以前のプロジェクト形式で取り込もうとしたらハマったことがあります。
できるだけAndroid Studioを使った方がよいです。sakura-bird1.hatenablog.com

2-3.パッケージ名とデフォルトアクティビティを登録する

f:id:sakura_bird1:20151003141029p:plain

パッケージ名とデフォルトアクティビティをそれぞれ記入します。
こちらは後から変更することが出来ます。
また、今は記入したくないという場合は、画面トップの「Skip Quick Start」ボタンをクリックして、アプリの設定画面から後から記入出来ます。

このパッケージ名を使用するか確認ダイアログが表示されますが、気にせず「Use this package name」をクリックします。

f:id:sakura_bird1:20151003141536p:plain

2-4. key hashを入力する

Development Key Hashesのところに開発者のPCで生成したハッシュ値を入力します。
リリース用のところはすでにkeystoreがあれば入力します。とりあえず今はDevelopmentの方だけでも大丈夫です。

ちなみに、Facebookサイトのやり方で生成したハッシュ値だと私の場合エラーになってしまいました。
現象はこちらと同じです。stackoverflow.com

仕方がないので、Javaコードからログを表示してそれを貼り付けました。

try{
        PackageInfo info = getPackageManager().getPackageInfo(
                "com.example.creeper", PackageManager.GET_SIGNATURES); // パッケージ名を自分のアプリにする
        for (Signature signature : info.signatures) {
            MessageDigest md = MessageDigest.getInstance("SHA");
            md.update(signature.toByteArray());
            Log.d("KeyHash:",Base64.encodeToString(md.digest(), Base64.DEFAULT));    // KeyHash:以降の文字列を使う

        }
    } catch (NameNotFoundException e) {

    } catch (NoSuchAlgorithmException e) {

}


入力できたら「Next」ボタンをクリックします。

f:id:sakura_bird1:20151003143150p:plain


2-5. クイックスタート終了→App IDとApp Secret取得!

ここまできたらもうアプリIDは出来ています。

タイトルバーの「My Apps」> 今作成したアプリをクリックします。
App IDとApp Secretを控えておき、Androidアプリ内で指定します。

2-6. Key Hashesの設定

タイトルバーの「My Apps」> アプリ > Android > Key Hashes
に2-4で記入したハッシュ値を入力しておきます。

2-7. Single Sign OnをYesにする

2-6のついでにSingle Sign OnをYesにしておいた方がよいかと思います。
Single Sign Onについてこちらのサイトで説明してくださってます。

nex.fm


「Save Changes」ボタンをクリックします。

f:id:sakura_bird1:20151003144841p:plain


以上です。

Facebookの有効期限の長いユーザーアクセストークンを取得する

developers.facebook.com

このエントリーではFacebookのアクセストークンのうちユーザーアクセストークンについてのみ扱います。
なおこの情報は2015/09/29に書いたものですので、Facebookの仕様変更により参考にならなくなっている可能性がありますのでご注意くださいませ。graph APIのバージョンはv2.4になります。
おかしな点がありましたら遠慮なくお知らせくださいませ。


FacebookでWebクライアントからログインダイアログを通じてユーザーがログイン、アクセス権限の許可をするとアクセストークンが取得できます。
有効期限は短く、約1〜2時間で切れます。
これとは別に有効期限の長いアクセストークンも存在し約60日の期間有効です。
AndroidiOSSDKで取得したアクセストークンはデフォルトで有効期限の長いものになります。

アクセストークンの寿命は警告なしで変わるかもしれないのでこの期間をあてにするべきではありません。
次のページにあるようにトークンが期限切れの場合の実装をしておくべきでしょう。

エラーをハンドリングする


下記に述べるAPIを使うことによって有効期限の短いユーザーアクセストークンを有効期限の長いユーザーアクセストークンに変換することができます。

なお、アクセストークンの期限を調べるにはそれ用のAPIもありますが、
アクセストークンデバッガを利用すると便利です。



通常の有効期限の長いアクセストークンの取得方法

変更のシーケンスはこの図のようになります。

https://scontent.xx.fbcdn.net/hphotos-xfa1/t39.2178-6/851576_1401207796761754_1304362567_n.png

1.Webクライアントがログインダイアログで有効期限の短いアクセストークンを取得

2.あなたのサーバーにアクセストークンを送信

3.あなたのサーバーからアクセストークンにApp IDとAppシークレットを加えFacebookに送信

4.Facebookが有効期限の長いアクセストークンを生成し返却

5.あなたのサーバーからクライアントに有効期限の長いトークンを送信

6.クライアントから(サーバーからでもよい)有効期限の長いアクセストークンを使用してGraph APIを実行

注意点としては、app secretがこのcallに含まれますので、クライアントからは実行せずサーバーから実行するようにしてください。
また変換用のアクセストークンは有効期間内のものにして下さい。

サーバーに送るパラメータなどはこのようになります。

GET /oauth/access_token?  
    grant_type=fb_exchange_token&           
    client_id={app-id}&
    client_secret={app-secret}&
    fb_exchange_token={short-lived-token} 

例えば次のようなアプリなら(ブログ用に一時的に作ったものでもう存在しないです)
アプリケーションID:878783778895290
アプリのシークレットキー:11f9cb4c3fa8d9bb8308061c7849f6a2

全体のURLはこのようになります。

https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id=878783778895290&client_secret=11f9cb4c3fa8d9bb8308061c7849f6a2&fb_exchange_token=ここに有効期限の短いアクセストークンを入れる

レスポンスはこのようになります。

access_token=CAAMfP8r5tboBAI09nQSriKEKMVBzHIoh2AEpuqWd2f2Vd0iEGgBVxxtB6DjTai0UxXpPx21axKc139QHGKk0ZBUv3k6eTyawMIPTjasMTtDWIZCF5SkTIkYFTYXAIRBZBhw78a6b6dp2m9To843p2TZBM1MWkae3SZAIn7c4HhZBk17FEXsXEPlEDuawntejkZD&expires=5183946

access_token=以下のアクセストークンは有効期限が約2ヶ月になっています。
上記の一連の作業はGraph ExplorerとAccessToken Debuggerを使って試すことが出来ます。
手前味噌で恐縮ですが、過去エントリーを参考にしてみてくださいませ。

sakura-bird1.hatenablog.com


変換後のトークンは使用しているSDKによりますが、
JavascriptSDKならFB.api()メソッドaccess_tokenパラメータを使用して保存するといいと思います。

よりセキュアなcallをするためにappsecret_proofというパラメータを利用できます。
利用方法は公式サイトをご覧ください。
Securing Graph API Requests - 参考資料 - 開発者向けFacebook







コード取得後有効期限の長いアクセストークンと交換する方法


同じユーザーが複数のWebクライアントからAPIをコールするにあたって、同じアクセストークンを使うべきではありません。
(例えば複数のコンピュータからログインした場合など)
サーバーに保存した期限の長いアクセストークンを使う代わりに、Facebookにコードを生成させて各クライアント毎に違うアクセストークンを生成させる方法があります。

1.ユーザー名とパスワードのような独自の認証システムを持っている
2.サーバーにFacebookの期限の長いアクセストークンを保存している
(ブラウザやモバイルアプリなどの別のクライアントからのcallに使用する)
3.全てのクライアントからAPIをコールする

もしこのようなアプリの場合、Facebookの自動スパムシステムの引き金になるのを防ぐべく
次のような処理をします。
最終的に各クライアントが独自の有効期限の長いアクセストークンを持っていることになります。

1.あなたのサーバーからアクセストークンをFacebookサーバーに送ってコードを取得する
(既に有効期限の長いアクセストークンを取得している前提です。)

2.あなたのサーバーからクライアントにコードを送信する

3.クライアントはFacebookサーバーにコードを送って有効期限の長いアクセストークンを交換する

4.クライアントはストーリーやクエリデータを投稿するのに有効期限の長いトークンを使用できる

変更のシーケンスはこの図のようになります。

https://scontent.xx.fbcdn.net/hphotos-xfa1/t39.2178-6/851584_503291546407012_1005168000_n.png

1.あなたのサーバーからアクセストークンをFacebookサーバーに送ってコードを取得する

サーバーに送るパラメータなどはこのような形式になります。

https://graph.facebook.com/oauth/client_code?access_token=...&client_secret=...&redirect_uri=...&client_id=...


リダイレクトURLというのは
デベロッパーコンソールのMy Apps > アプリを選ぶ > 設定 > Website > Site URL
に設定したURLを指定します。

client_idはアプリケーションID
client_secretはアプリのシークレットキーを指定します。


レスポンスはこのようになります。

{"code":"AQBpFLvhZFkZrHNJf9GiLPmpCRlbjv0EweEqYXCnES5mxBkWOZ0pwHiWZcRY4meZVed7UuJTl3WkajxuVmg2ckORkPcmRLeivE1dRLXtK-t-EMm5AI8Lp958QLU-uXB6FMahfmnii00FnKiqSDNdzc7nyUN5zTONMIsis_MqsnhzteV9L9eGi9MnIBz0Qcflfq7iQfn_sq_8Y-uEVhpQbdeO3Ez77q7JJwpePVGL7WfOLwpOe9vgKdg1b4USNQbEx08necyxaDsnyqQbbRNa19xwWTzIASHgLWXk6ZMiyE85kIN1Dxz8uscbkT-WANIVhn-n_15E1gSpYw_lKlYY0Ldg2EgoLxLYRAmeD3RKdYwLLA"}

2.あなたのサーバーからクライアントにコードを送信する

1.で取得したcodeをクライアントに送ります。

3.クライアントはFacebookサーバーにコードを送って有効期限の長いアクセストークンを交換する

サーバーに送るパラメータなどはこのような形式になります。

https://graph.facebook.com/oauth/access_token?code=...&client_id=...&redirect_uri=...&machine_id= ...

machine_idというのはクライアント毎にFacebookサーバーが発行するものです。当然初回は指定する必要はありません。
次回からセットします。


レスポンスはこのようになります。

{"access_token":"CAAMfP8r5tboBAPSF4wvPR3dFdSdAFcClkM4yn0HwHKLU3sBWjKJAaNrYOhpmuMZA9p5o93dqUgSWTVeo1xZBVTno74kzl1r94qX3mu4jI1dd7QSc7jtCAvw4ZB91LZBwdsdu08vDiAU148ZCVmwW848WnG1ljSSs41mWYq1f8apGlaOfdDdeJczATpvYyTY0ZD","machine_id":"XVbxVah5bqsimnKHKHWs5o3z","expires_in":5177575}

有効期限の長いアクセストークンが返ってきました。