webviewでGoogleのOAuth認証を行う

# 概要
webviewを用いたGoogleのOAuth認証についての四苦八苦を記載します。後に述べますが、webviewからの従来の認証手順は使えなくなり、複雑なステップを踏む必要があります。冗長記述部分がありますので、結論が知りたい方は[対応](http://qiita.com/gambare/items/7af00ad4c953aed9b11e#対応)からご覧下さい。
Angularfire2を使用する手順を説明する日本語記事が少ないため、別途必要に応じ記載します。ここでは薄く触れます
なお、今回の前半に記載するweb用の設定は、Angular(2.0以降)でも流用可能な部分が多いです。

# 事前準備
以下のインストールなどを既に終えていることを前提とします。

- Android SDKのインストール (Angularなら不要)
- Android端末の用意またはavdシミュレータのインストール (Angularなら不要)
- ionicと、cordovaまたはAngular(2.0以降)のインストール
- firebaseプロジェクトの作成

# 環境構築

ionic2(またはwebアプリのみであればAngular(2.0以降))プロジェクトを作成し、AngularFire2をインストールします。

## AngularFire2について

参考: https://github.com/angular/angularfire2

firebaseをAngularやionic2で使用するためのライブラリです。firebaseから取得したデータをObservableで取得したりします。

## ionic2プロジェクト作成
以下コマンドでionic2プロジェクトを作成します。
既にプロジェクトがある場合不要です。

```bash
ionic start --v2 <プロジェクト名>
# angular cliの場合 ng new <プロジェクト名>
cd <プロジェクト名>
```

### Angularfire2インストール
Angularfire2及びfirebaseライブラリを以下のコマンドでインストールします。
既にインストール済みの場合不要です。

```bash
npm install angularfire2 firebase --save
```

## firebaseの設定
firebaseへのログインでGoogle OAuthを使用できる様に設定します。
既に設定済みの場合は不要です。


### Google OAuthを使用可能にする

[firebaseコンソール](http://firebase.google.com/?hl=ja)から、作成したfirebaseプロジェクトを選択します。
左ペインから`Authentication`を選択します

![](https://qiita-image-store.s3.amazonaws.com/0/37910/d7e4e002-69ae-1f34-acfa-0828f7552296.png "")


Authenticationが表示されます。`ログイン方法`タブを選択

![](https://qiita-image-store.s3.amazonaws.com/0/37910/32a2763b-5555-0b52-c94a-c4f768e96757.png "")

`プロバイダにログイン`の中から、`Google`を選択し`有効にする`トグルをONにして、保存ボタンを押します


これでGoogle OAuthでfirebaseにログインできる様になったはずです。
尚、firebaseへのログインは、以下の方法をとることも可能です。
(試してません。今回は`Google`のみ説明します。)

- メールアドレスとパスワード
- Facebook
- Twitter
- GitHub
- 匿名

![](https://qiita-image-store.s3.amazonaws.com/0/37910/4968b64f-fc3a-2895-c86d-4d2bd0c8dfaf.png)


### apiKeyなどを取得する

firebaseのトップメニューから`ウェバプリにFirebaseを追加`をクリックします。
(すでにアプリを追加している場合は、`別のアプリを追加`をクリックし、`ウェブ`を選択します。)
![](https://qiita-image-store.s3.amazonaws.com/0/37910/fe6d203b-8235-1a41-456f-896657989768.png)

`ウェブアプリに Firebase を追加`ウィンドウから、`apiKey`,`authDomain`, `databaseURL`, `storageBucket`を取得し、記録しておきます。すぐ使います。

# コーディング(webアプリ向け)
Angularfire2を使用したfirebase,Googleの認証は以下の様なコードを記載します。


```ts:app.module.ts
import {AngularFireModule} from 'angularfire2';

~省略~
@NgModule({
  declarations: [
	~省略~
  ],
   imports: [
    AngularFireModule.initializeApp(
      firebaseConfig,
      firebaseAuthConfig
    ),
  ],
  ~省略~


// 以下の値は、apiKeyなどの取得で取得した値。
export const firebaseConfig = {
  apiKey: 'APIキー',
  authDomain: 'ドメイン',
  databaseURL: 'URL',
  storageBucket: 'ストレージバケット'
};

// 以下の値は、認証設定。Google認証をリダイレクトで行う。
export const firebaseAuthConfig = {
  provider: AuthProviders.Google,
  method: AuthMethods.Redirect
};

```

```ts
import { AngularFire,
         FirebaseAuthState} from 'angularfire2';

@Component({
  templateUrl: 'テンプレート名.html'
})
export class Test{

  constructor(private af :AngularFire){}

  // ログインメソッド。templateのボタンクリックで呼び出される。
  login() :firebase.Promise<FirebaseAuthState>{
    return this.af.auth.login();
  }

}
```

```html:template
<!-- ページにボタンを追加します。前後のコードは省きます。 -->
<button ion-button (click)="login()">Login</button>
```

コーディングはこんな感じです。

# webアプリとして起動

以下のコマンドによりwebアプリケーションとして起動します。

```bash
ionic serve
# Angularの場合 ng serve
```

ページにボタンが表示されます。クリックするとログインすることができます。

![](https://qiita-image-store.s3.amazonaws.com/0/37910/bb216ed6-e1b8-ef79-b54a-c935bb326561.png)

# Android用設定

web appとして作成したプロジェクトをAndroidのwebviewとして動作する様に対応します。


## Androidプラットフォーム登録

ionicプロジェクトでAndroidを使用するには以下コマンドでAndroid用の設定を追加します。
(ios用設定は、mac環境であればデフォルトでインストールされます。)

```bash
ionic platform add android
```

## 起動(ログイン失敗)
Android端末をUSB接続し開発者オプション設定を行ったのち、アプリケーションのテストbuild及びインストールを行います

```bash
ionic run android --consolelogs --livereload --device
# avdで起動する場合、 --deviceは不要
```

これにより、apkの作成及びインストールが行われますが`LOGIN`ボタンを押しても、Androidブラウザが起動したり、全く反応がなかったり、とうまく動きません。

### 調査

Authメソッドには以下があります。

[参考](https://angularfire2.com/api/enums/_providers_auth_backend_.authmethods.html#oauthtoken)

- Anonymous
- CustomToken
- OAuthToken
- Password
- Popup
- Redirect

手っ取り早く試せるPopupも試しましたが動作しません。

### やや脱線
facebookのOAuth認証にはcordovaプラグインが必要であるとの情報をいくつか見つけました

https://github.com/aaronksaunders/basicaf2
https://github.com/aaronksaunders/AngularFire2-Ionic2-Facebook
https://firebase.google.com/docs/reference/js/firebase.auth.FacebookAuthProvider#credential

Google認証についても調査してみます。
ng2-cordova-oauthというライブラリを発見しました。
https://github.com/nraboy/ng2-cordova-oauth

が、読み進めていくと・・・
https://github.com/nraboy/ng2-cordova-oauth#important-note-about-google
GoogleのOAuth認証は、webviewをブロックする旨の記事にぶちあたりました。ぎゃあす。

Google公式(英語):
[Modernizing OAuth interactions in Native Apps for Better Usability and Security](https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html)

Google公式(日本語):
[ネイティブ アプリの OAuth インタラクションを最新にしてユーザビリティとセキュリティを向上する](https://googledevjp.blogspot.jp/2016/09/modernizing-oauth-interactions-in-native-apps.html)

要約すると、

- webviewからのOAuth認証はセキュアじゃないがあるからブロック
- 2016/10/20から段階的にブロック
- 2017/04/20に原則ブロック
- 代替手段は、
  - Google Sign-In
  - AppAuth

とのことでした。ぎゃあす。

参考:
[OAuthの認証にWebViewを使うのはやめよう](https://shogo82148.github.io/blog/2012/11/24/no-more-webview/)


# 対応

色々調べていくと、cordovaのプラグイン`cordova-plugin-googleplus`を使用することで、webviewからOAuth認証を行うことが可能であることを確認しました。(まだ確認中の部分もありますが)

[cordova-plugin-googleplus](https://github.com/EddyVerbruggen/cordova-plugin-googleplus)


## プラグインの概要

- iOSとAndroidで、Google Sign-inを使用してユーザ認証します。
- email, display name, given name, family name, profile picture urlと user idを取得します。
- Google Sign-In APIをラップしてるだけなので、ユースケースや開発に従い修正すべきだそうです。
- Android向けとiOS向けのプロジェクトは同一にすることが強く推奨されています。
- Androidの場合、(恐らく)端末のアカウントを使用するため、ログインという処理は不要の様です。

## 改修前の準備

Typescriptのプロジェクトディレクトリにある、`config.xml`を開き、2行目くらいにある
`パッケージid`を確認します。このIDを後に使用します。

```xml:config.xml
<?xml version='1.0' encoding='utf-8'?>
<widget id="ここです。" ...>
...
</widget>
```

## iOSの対応

<b>注意：iOSはテスト未実施です。誤りがある場合、別途修正します。</b>

### `REVERSED_CLIEND_IDの取得`

まず、`REVERSED_CLIENT_ID`(`iOS URL Scheme`)を取得します。(iOSのみ必要)
firebaseを使用する場合[firebaseコンソール](http://firebase.google.com/?hl=ja)のfirebaseプロジェクトから登録を行います。
トップメニューから`iOSアプリにFirebaseを追加`を選択します。
![](https://qiita-image-store.s3.amazonaws.com/0/37910/fe6d203b-8235-1a41-456f-896657989768.png)


ここのiOSバンドルIDに、先ほど取得した`パッケージid`を指定し`アプリを追加`すると、自動的にダウンロードされる`GoogleService-Info.plist`に、`REVERSED_CLIENT_ID`の記載があります。これを記録します。
![](https://qiita-image-store.s3.amazonaws.com/0/37910/3a3e5000-8027-4c8f-bf4b-0fe65380db09.png "")

```plist:GoogleService-Info.plist
<key>REVERSED_CLIENT_ID</key>
<string>ここです</string>
```


## Androidの対応

iOSと同様に[firebaseコンソール](http://firebase.google.com/?hl=ja)から登録を行います。


### OAuthクライアントIDの取得

トップメニューから`AndroidアプリにFirebaseを追加`を選択します。
![](https://qiita-image-store.s3.amazonaws.com/0/37910/fe6d203b-8235-1a41-456f-896657989768.png)
ここのパッケージ名に、先ほど取得した`パッケージid`を指定し`アプリを追加`をすると、自動的にダウンロードされる、`google-services.json`に、`REVERSED_CLIENT_ID`の記載があります。これを記録します。
![](https://qiita-image-store.s3.amazonaws.com/0/37910/b49106a8-6fb3-ba40-1978-2c5bb2f15af4.png)


```json:google-services.json
      "oauth_client": [
        {
          "client_id": "ここです",
          "client_type": 3
        }
```


### クライアント認証キーの取得

[参考](https://developers.google.com/android/guides/client-auth)
keytoolを使用して、クライアント認証用のハッシュ値(SHA-1)を取得します。

```bash
keytool -exportcert -list -v \
-alias androiddebugkey -keystore ~/.android/debug.keystore
```

デバッグ用パスワードのデフォルトは`android`です。これにより以下の様に認証キーを取得できます。

```
SHA1: 〜〜〜〜ハッシュ値〜〜〜〜
```

### 認証キーの登録

再び、[firebaseコンソール](http://firebase.google.com/?hl=ja)に戻り、トップメニューのAndroidから`管理`をクリックし、
![](https://qiita-image-store.s3.amazonaws.com/0/37910/3d5a4a1e-3100-136d-3ad2-cac8be4aadbb.png)

フィンガープリントを追加ボタンから、取得した認証キーを登録します。
![](https://qiita-image-store.s3.amazonaws.com/0/37910/656c1733-77f0-890e-7344-7923f111d365.png)


## cordovaプラグイン登録

漸くプラグインの登録を行います。以下のコマンドをTypescriptのプロジェクトディレクトリで実行します。

```bash
# npmから取得する場合
cordova plugin add cordova-plugin-googleplus --save --variable REVERSED_CLIENT_ID=ここにiOSの手順で取得したIDを入れる

# githubから最新を取得する場合
cordova plugin add https://github.com/EddyVerbruggen/cordova-plugin-googleplus --save --variable REVERSED_CLIENT_ID=ここにiOSの手順で取得したIDを入れる

cordova prepare
```

## ログインメソッドの修正

参考：
https://forum.ionicframework.com/t/how-to-add-an-ionic-2-google-login/51114/10

先述の通り、Androidのwebview上でも既にGoogleのOAuthにログインしている状態ですが、ログインボタン押下で認証情報をダイアログに表示するように改修してみます。

```ts
 login(){
     window['plugins'].googleplus.login(
         {
           // 'scopes': '',
           'webClientId': 'ここにAndroidの手順で取得したgoogle-services.jsonのclient-idを入れる',
           'offline': false,
         },
         function (authData) {
           alert(JSON.stringify(authData)); // do something useful instead of alerting
         },
         function (msg) {
           alert('error: ' + msg);
         }
     );
   }
```

## アプリの起動

再び以下のコマンドでアプリを起動します。

```bash
ionic run android --consolelogs --livereload --device
```

ログインボタンを押した時に認証情報をアラート表示できれば、まずはGoogle OAuthの認証に成功しました。

## firebaseへのログイン

Google OAuthでログインした情報を使用し、firebaseへのログインを試みます。
認証データのidTokenを用い、`GoogleAuthProvider`の[credential](https://firebase.google.com/docs/reference/js/firebase.auth.GoogleAuthProvider#.credential)メソッドを呼び出し、[firebase.auth.AuthCredential](https://firebase.google.com/docs/reference/js/firebase.auth.AuthCredential.html)を取得します。
このcredentialを用いて、[signInWithCredential](https://firebase.google.com/docs/reference/js/firebase.auth.Auth#signInWithCredential)メソッドによりサインインを行います。

これをngOnInitメソッドで使ってみます

```ts
  ngOnInit(){
    window['plugins'].googleplus.login(

        {
           // 'scopes': '',
           'webClientId': 'ここにAndroidの手順で取得したgoogle-services.jsonのclient-idを入れる',
           'offline': false,
        },
        function (authData) {

          let credential = (<any>firebase.auth.GoogleAuthProvider).credential(authData.idToken);
          firebase.auth().signInWithCredential(credential);

        },
        function (msg){
          console.log('acquireAuth error: ' + msg);
        }
    );
  }
```

この処理により、ログインができました。
ログインの確認は、コンストラクタで定義しているAngularfireサービスを用いて`this.af.auth`で認証を取得できます。


2016/12/26追記：
------

書き忘れましたすみません　:P
このプラグイン「cordova-plugin-googleplus」は、cordova CLIで起動する必要があります。(ionic cliではうまく動きません。)
以下のようにcordova cliから起動することはできますが、このコマンドではコンソール(ターミナル)にログが出力できません。

```bash
cordova run android --device
```

そのため、テストには不向きです。OSに従い、以下の手順を実施してください。


## Android

コンソールへのログ出力を行うためには Android SDKを使用します。
Android SDKのインストール方法は、cordovaの以下を参照願います。
https://cordova.apache.org/docs/ja/latest/guide/platforms/android/

Android SDKを開いたら、右ペインから「Import project (Eclipse ADT, Gradle, etc.)」を選択し、
![image](https://qiita-image-store.s3.amazonaws.com/0/37910/4de573e5-cdb4-b4f1-6642-1a1f1e8e9255.png)

`{プロジェクトディレクトリ}/platforms/android`を指定し、Debugで起動してください。
Android SDK上にログが表示されます。

### ios

`{プロジェクトディレクトリ}/platforms/ios/{プロジェクト名}.xcodeproj`
を実行し、xcode上からrunしてください。(未実施)