AndroidでOAuth認証をしたい

タイトルのまんまなのですが、Androidで、OAuth2.0認証をしたくなったので、調べてみました。なかなか理解できませんでしたが、何とか実装することができました。自分が理解したことをまとめてみたいと思います。
なお、私は、WebアプリケーションでOAuth1.0認証を行った経験はありましたので、OAuthの概要については、理解しているつもりでした。

# 難しかったところ
簡単に沢山の情報が見つかり、それぞれ分かりやすく具体的に書かれていましたが、どれも実装方法が異なり、どの実装方法を参考にすれば良いか、わかりませんでした。
実装方法が異なるのは、アプリケーションの形態や、ユーザーへの見せ方、使用ライブラリなどが異なるためでした。

# 実装パターン一覧
調べてみた実装方法について、自分なりに解釈した内容を表にまとめてみました。今回は、Androidアプリの実装方法に注目していますが、比較のため、Webアプリケーションとコンソールアプリケーションも記述しています。私がざっと調べて見つけた実装方法のみ記述していますので、探せば他にも色々あると思います。

|パターン番号|アプリケーション形態|接続先|認証画面UI|ライブラリ|ユーザ操作|
|:--|:--|:--|:--|:--|:--|
|1|Androidアプリ|各Webサービス（コールバック）|WebView|未使用 or 各種ライブラリ|ID・パスワード入力|
|2|〃|〃|ブラウザアプリ|〃|〃|
|3|〃|各Webサービス（OOB）|ブラウザアプリ|〃|ID・パスワード入力。PINのコピー＆ペースト。|
|4|〃|Googleサービス|ブラウザアプリ|Google APIs Client Library for Java ※1,2|ID選択。パスワード不要。|
|5|〃|Googleサービスのみ？|Android OS|Android SDK (AccountManager)|ID選択。パスワード不要。|
|6|Webアプリケーション|各Webサービス（コールバック）|ブラウザ|未使用 or 各種ライブラリ ※2|ID・パスワード入力|
|7|コンソールアプリケーション|各Webサービス（OOB）|ブラウザ|〃|ID・パスワード入力。PINのコピー＆ペースト|

※1　「Google APIs Client Library for Java」と同様のライブラリとして「Google Data Java Client Library（gdata-java-client）」というものもありました。「Google Data Java Client Library（gdata-java-client）」は、2012年3月頃で開発が止まっているようで、「Google APIs Client Library for Java」の前世代のライブラリのようでした。

※2　「Google APIs Client Library for Java」は、Androidアプリでも、Webアプリケーションでも使えます。たぶん、コンソールアプリケーションでも使えると思います。使う場合は、Androidアプリの場合、GoogleAccountCredentialクラス、GoogleAuthUtilクラスを使います（GoogleAccountCredentialクラスは内部でGoogleAuthUtilクラスを使用）。Webアプリケーションの場合、GoogleAuthorizationCodeFlowクラスを使います。

Androidアプリにおける実装パターン１～５について、画面イメージは次のようになります。

# 実装パターン１
アプリケーション形態：Androidアプリ
接続先：各Webサービス（コールバック）
認証画面UI：WebView
ライブラリ：未使用、各種ライブラリ使用のいずれでも実装可能
ユーザ操作：ID・パスワード入力

自作アプリです。OAuth認証を行うきっかけを用意しておきます。ここでは、開始ボタンを用意しました。
![device_WebViewTwitter10a_01.png](https://qiita-image-store.s3.amazonaws.com/0/24511/b51f0e00-4cb4-95c3-3383-09a63c228be1.png)

自作アプリの内部に、WebViewを使用して、各Webサービスの認証画面を表示します。ここでは、Twitterの認証画面を表示しています。認証画面を表示する時には、コールバックURLを設定します。コールバックURLは、認証完了後に呼ばれるURLで、自作アプリ用のURLを設定しておきます。ユーザは、アカウントのIDとパスワードを入力し、アクセス権限を確認します。
![device_WebViewTwitter10a_02.png](https://qiita-image-store.s3.amazonaws.com/0/24511/dceb44bb-2612-fe93-512b-37ce918a38fc.png)

認証後、自作アプリから、各Webサービスの情報にアクセスできるようになります。ここでは、アカウント情報を取得し、画面表示してみました。
![device_WebViewTwitter10a_03.png](https://qiita-image-store.s3.amazonaws.com/0/24511/e618b935-f02c-75d1-c49d-5dcb60fcddba.png)

この実装パターンは、Androidアプリでもよく見かける気がしますし、Webアプリケーションにおいても同じ流れになりますので、ユーザーにとっては、慣れている操作手順かもかもしれません。

ただし、セキュリティ的な危険性を指摘するブログなどを見かけました。悪意ある開発者であれば、自作アプリ内のブラウザで入力されたアカウントのIDとパスワードを盗み取ってしまえるらしいです。ですが、個人的には、自作アプリなり、開発チームや会社などが、ユーザから既に信頼を得られているのであれば、この方式でも概ね受け入れられている気がします。

# 実装パターン２
アプリケーション形態：Androidアプリ
接続先：各Webサービス（コールバック）
認証画面UI：ブラウザアプリ
ライブラリ：未使用、各種ライブラリ使用のいずれでも実装可能
ユーザ操作：ID・パスワード入力

基本的には、実装パターン１と同じですが、WebViewの替わりに、Android端末内にインストールされているブラウザアプリを呼び出す方式です。

自作アプリです。開始ボタンをタップするとブラウザアプリを起動します。
![device_BrowserIntentTwitter10a_01.png](https://qiita-image-store.s3.amazonaws.com/0/24511/ffed62ff-172b-fcf3-b24c-5130846b3f9f.png)

端末内に複数のブラウザアプリがインストールされ、デフォルトが決められていない場合、ブラウザアプリを選択するダイアログが出ます。認証完了後に自作アプリが呼ばれるように実装しておきます。
![device_BrowserIntentTwitter10a_02.png](https://qiita-image-store.s3.amazonaws.com/0/24511/bb2c3b0e-df4d-aa1a-3af2-d4560908ac61.png)　![device_BrowserIntentTwitter10a_03.png](https://qiita-image-store.s3.amazonaws.com/0/24511/280f0bb1-ea3e-7d1e-8dd5-9353e5f14718.png)

認証後、各Webサービスの情報にアクセスできます。
![device_BrowserIntentTwitter10a_04.png](https://qiita-image-store.s3.amazonaws.com/0/24511/67bd2fe9-6839-90f9-97b6-4ac2e758435b.png)

この実装パターンは、WebViewを使った時と比べて、セキュリティ面が改善されています。ブラウザアプリに入力されたアカウントのIDとパスワードを盗み取ることが困難なためです。

# 実装パターン３
アプリケーション形態：Androidアプリ
接続先：各Webサービス（OOB）
認証画面UI：ブラウザアプリ
ライブラリ：未使用、各種ライブラリ使用のいずれでも実装可能
ユーザ操作：ID・パスワード入力と、PINのコピー＆ペースト

実装パターン２と比較して、各Webサービスの画面から、自作アプリへの戻り方が異なります。

自作アプリです。ここでは、連携確認ボタンをタップするとブラウザが起動するようにしました。
![device_BrowserPinTwitter10a_01.png](https://qiita-image-store.s3.amazonaws.com/0/24511/3c397072-7079-6546-e9df-4864b87382b9.png)

各Webサービスで認証後、PINと呼ばれるコードが払い出されます。ユーザにPINをコピーしてもらい、自作アプリへ戻ってもらいます。
![device_BrowserPinTwitter10a_02.png](https://qiita-image-store.s3.amazonaws.com/0/24511/d41e1289-a421-752b-04cc-f8acd9a65c43.png)　![device_BrowserPinTwitter10a_03.png](https://qiita-image-store.s3.amazonaws.com/0/24511/cdb0f83d-62e3-e9f0-bb22-b9f89ebf6400.png)　![device_BrowserPinTwitter10a_04.png](https://qiita-image-store.s3.amazonaws.com/0/24511/4aa5149a-db65-aaef-88d6-b5f08245264c.png)

自作アプリに先ほどのPINを貼り付けてもらいます。このPINを使って、各Webサービスに接続し、情報にアクセスできるようになります。
![device_BrowserPinTwitter10a_05.png](https://qiita-image-store.s3.amazonaws.com/0/24511/6550e54c-81bb-6f74-78ee-5aa13765fe74.png)　![device_BrowserPinTwitter10a_06.png](https://qiita-image-store.s3.amazonaws.com/0/24511/61c36ee9-b360-0600-2532-735027e56bf4.png)

PINを使うことで、各Webサービスと自作アプリの連携が少なくなりますので、セキュリティ面が安全になるようです。ただし、ユーザの操作性が悪くなるため、個人的にはあまり使いたくない実装パターンだと思います。

なお、ブラウザアプリの替わりに、WebViewを使用して実装することもできます。しかしながら、PINを使うことのセキュリティ面のメリットが失われてしまいますので、あまり意味がないと思います。

# 実装パターン４
アプリケーション形態：Androidアプリ
接続先：Googleサービス
認証画面UI：ブラウザアプリ
ライブラリ：Google APIs Client Library for Java 使用
ユーザ操作：ID選択。パスワード不要。

実装パターン４は、今まで説明した実装パターンとは、だいぶ異なります。
この実装パターンが使えるのは、Android端末で、Googleアカウントのみです。

自作アプリです。
![GoogleApisClient01.png](https://qiita-image-store.s3.amazonaws.com/0/24511/2ac1b499-0c11-5441-abb1-ecf15bb52604.png)

まず、認証に使用するGoogleアカウントを決定します。ここでは、Android SDKのAccountManagerを使用し、Androidの機能を呼び出して、端末に登録されているGoogleアカウントから、今回使用するアカウントを選択してもらいました。新規にGoogleアカウントを端末に追加して、使用することもできるようです。Googleアカウントを選択するだけで、パスワードの入力は不要です。（なぜ、パスワードがいらないのだろう？あまり理解できていないです。）
![GoogleApisClient02.png](https://qiita-image-store.s3.amazonaws.com/0/24511/36bb592c-5bbe-d3f1-70e8-b29766dae4bc.png)　![GoogleApisClient03.png](https://qiita-image-store.s3.amazonaws.com/0/24511/d2fc224f-6f64-e14d-6312-8760550415f4.png)

認証後、Googleサービスの情報にアクセスできます。
![GoogleApisClient04.png](https://qiita-image-store.s3.amazonaws.com/0/24511/0fbcddc0-ce76-b9fe-dd4f-f98199babfb5.png)

この実装パターンでは、ユーザ操作がかなり簡単ですね。文字を入力することがなく、数回タップするだけです。
ただし、セキュリティ面はどうでしょう？端末を紛失して、悪意ある人に使われてしまうと、勝手に認証操作を行われてしまいそうです。とはいえ、端末を奪われた時点で、もっと大きな問題が発生するでしょうから、他者に操作される懸念は別の観点で考えたほうがよいのではないでしょうか。

# 実装パターン５
アプリケーション形態：Androidアプリ
接続先：Googleサービスのみ？
認証画面UI：Android OS
ライブラリ：Android SDKのAccountManager使用
ユーザ操作：ID選択。パスワード不要。

実装パターン５は、ユーザにとって、実装パターン４と似たように見えるかもしれませんが、実装は別物です。認証情報が管理される場所も異なります。実装パターン１～４では、各Webサービスのサーバ側で認証情報が管理され、各WebサービスがWeb上で提供しているアカウント管理画面から認証を解除することができました。しかし、実装パターン５では、Android端末側で管理されるようです。Web上のアカウント管理画面を見ても、自作アプリとアカウントが紐づいていることが分からない状態となっていました。どうしたら認証状態を確認できるのか、どうしたら解除できるのか、わかりませんでした。

自作アプリです。
![AccountManager01.png](https://qiita-image-store.s3.amazonaws.com/0/24511/38e9d8c4-8740-8ea9-650e-7bdab22aab25.png)

AccountManagerが、必要に応じて、Account選択ダイアログを出したり、認証ダイアログを表示してくれます。Googleアカウントを選択するだけで、パスワードの入力は不要です。
![AccountManager02.png](https://qiita-image-store.s3.amazonaws.com/0/24511/f5ae6251-d997-c562-366b-401aa82d4c41.png)　![AccountManager03.png](https://qiita-image-store.s3.amazonaws.com/0/24511/3ab1dea8-0ae4-3523-e0f6-11d88a183d25.png)

認証後、Googleサービスの情報にアクセスできます。
![AccountManager04.png](https://qiita-image-store.s3.amazonaws.com/0/24511/691df30f-b3a8-91ab-35f0-6760bc887f2e.png)

この実装パターンでも、実装パターン４と同様に、ユーザ操作がかなり簡単ですね。文字を入力することがなく、数回タップするだけです。

セキュリティ面では、実装パターン４と同じ問題がありまそうです。Androidが進化を続けても、この実装パターンは、使い続けられるのでしょうか？よくわかりません。私の理解不足が多分にあるため、どう扱うべきか決めかねています。

# まとめ
以上のように、AndroidアプリにおけるOAuth認証の実装方式は、すくなくとも5パターンありました。しかも、探せばもっと他の実装パターンもあるかもしれません。Webアプリケーションでは、概ね1パターンであったのと比べるとかなり違いますね。

説明した実装パターンについて、比較してみます。

|パターン番号|適用範囲|操作性|セキュリティ|
|:--|:--|:--|:--|
|1|○|○|△|
|2|○|○|○|
|3|○|△|◎|
|4|△|◎|○|
|5|△|◎|○|

Googleサービスのみを使う場合は、実装パターン４で実装すると、使い勝手がよさそうです。実装パターン５も同様ですが、私があまり理解できていないので選択肢から外しています。Googleサービス以外のサービスを使う場合は、実装パターン２がよさそうです。

以上、ひとまず自分の理解をまとめてみました。もし、誤りなどありましたら、コメント頂けるとありがたいです。

# 動作確認
確認環境

- Android 4.0.3
- Google OAuth 2.0
- Twitter OAuth 1.0a

確認用プログラム

- [TryAndroid/OAuth03 at master · kurukurupapa/TryAndroid](https://github.com/kurukurupapa/TryAndroid/tree/master/OAuth03)

# 参考ページ

各Webサービス

* [Twitter Developers](https://dev.twitter.com/)
* [Google Developer Console](https://console.developers.google.com/)

ライブラリ

* [fernandezpablo85/scribe-java](https://github.com/fernandezpablo85/scribe-java)
* [Google APIs Client Library for Java](https://developers.google.com/api-client-library/java/)
* [Google OAuth2.0 for scribe-java](https://gist.github.com/yincrash/2465453#file-google2api-java)

実装方法

* [OAuth 2.0でWebサービスの利用方法はどう変わるか（1/3）- ＠IT](http://www.atmarkit.co.jp/fsmart/articles/oauth2/01.html)
* [開発日誌 (7) : scribeでGoogle OAuth 2.0 (client_secretなしで認証) - 家族ToDo(仮)開発日誌](http://kazokutodo.hatenablog.com/entry/2014/02/10/011927)
* [OAuthの認証にWebViewを使うのはやめよう - Shogo's Blog](http://shogo82148.github.io/blog/2012/11/24/no-more-webview/)
* [AndroidからGoogle OAuthでプロフィール情報にアクセスする方法 - 今日の役に立たない一言 － Today’s Trifle! －](http://d.hatena.ne.jp/satoshis/20130119/p1)
* [Twitter4jを使ってOAuth認証をアプリ内で行う方法 - 素人のアンドロイドアプリ開発日記](http://andante.in/i/android%E3%82%A2%E3%83%97%E3%83%AAtips/twitter4j%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6oauth%E8%AA%8D%E8%A8%BC%E3%82%92%E3%82%A2%E3%83%97%E3%83%AA%E5%86%85%E3%81%A7%E8%A1%8C%E3%81%86%E6%96%B9%E6%B3%95/)
* [AndroidでのGoogle Account OAuth認証方法 - Qiita](http://qiita.com/skonb/items/97683c9b0522d753b870)
* [Android - Google APIs Client Library for JavaからGoogle Drive APIを使用する - Qiita](http://qiita.com/kubotaku1119/items/9df79c568e100c0c7623)
* [琴線探査: AndroidのAccountManager経由でGoogleのOAuth2認証を行うには？（外部ライブラリ完全非依存版）](http://kinsentansa.blogspot.jp/2012/08/androidaccountmanagergoogleoauth2.html)

# 変更履歴
2014/11/10 「Google APIs Client Library for Java」「Google Data Java Client Library（gdata-java-client）」についての注釈を追記しました。
2014/11/21　実装パターン５（AccountManager使用）を追加しました。