Firebase Auth のユーザ認証機能を自前のデータベースと連携する

認証だけfirebase使いたい場合の考察。

## 要求

* ユーザ認証（登録・ログイン）はfirebaseに任せたい。
* ユーザデータはAWS RDS等別途のシステムで管理したい。


## 全体像

1. Firebase <-> client ... ユーザ登録・ログイン（**アクセストークン**の発行）
2. client -> self-backend ... **アクセストークン**の送信
3. self-backend <-> Firebase ... **アクセストークン**検証、**uid**（firebase user ID）の確認
4. self-backend -> client ... 認証可否含めたレスポンスの返却


## 詳細

### 1. Firebase <-> client ... ユーザ登録・ログイン（**アクセストークン**の発行）

* **firebaseとclient間のみのやりとりでユーザ認証をしアクセストークンを得る**
* ドキュメント通り実装する https://firebase.google.com/docs/auth/?hl=ja
 * 必要なプロバイダー(facebook, twitter, emailなど)毎の実装
 * ios/android/webどれでも対応可能

### 2. client -> self-backend ... **アクセストークン**の送信

* **認証完了した際に得られるアクセストークンを自サービスのbackendに投げる**
* 自前backendにリクエストする際に`Authorization: Bearer {TOKEN}`httpヘッダつけるのが素直な実装か。
* その際、アクセストークンはclientプログラムで保持せず、毎回getTokenを使うとリフレッシュ等自動でやってくれる（はず）

### 3. self-backend <-> Firebase ... **アクセストークン**検証、**uid**（firebase user ID）の確認

* **Firebaes Admin SDKを使ってアクセストークンを検証し、uidを得る**
* https://firebase.google.com/docs/admin/setup?hl=ja
 * 例えばphp（サードパーティ製） -> https://github.com/kreait/firebase-php

 ```php

// token の検証をしてuidを返す
function verifiedUid($idTokenString)
{
    if (empty($idTokenString)) {
        return '';
    }
    // firebase管理画面からadmimn jsonをDL、配置しておく
    $firebaseServiceJson = json_decode(file_get_contents('path/to/firebase/admin/sdk/settings.json'));
    // PSR16 Simple Cacheの実装であれば他のでも大丈夫そう。firebaseのpublic keyをキャッシュする用
    $cache = new \Symfony\Component\Cache\Simple\FilesystemCache();
    $keyStore = new \Firebase\Auth\Token\HttpKeyStore(null, $cache);
    $verifier = new \Firebase\Auth\Token\Verifier($firebaseServiceJson->project_id, $keyStore);

    $uid;
    try {
        $verifiedIdToken = $verifier->verifyIdToken($idTokenString);
        $uid = $verifiedIdToken->getClaim('sub'); // "uid"

        // なんらかのキャッシュ機構を使って毎回firebaseにverifyしないようにする方が良さそう
        $exp = $verifiedIdToken->getClaim('exp'); // expr

    } catch (\Firebase\Auth\Token\Exception\ExpiredToken $e) {
        var_dump($e);
    } catch (\Firebase\Auth\Token\Exception\IssuedInTheFuture $e) {
        var_dump($e);
    } catch (\Firebase\Auth\Token\Exception\InvalidToken $e) {
        var_dump($e);
    }
    
    return $uid;
}
```
* 得られたuidはfirebase project単位でのユニーク値なので、この値を元に自前db等でデータ管理する。uidでの登録がなければ新規ユーザエントリ生成。あれば既存ユーザのログインとなる。
* MySQLでのシンプルなテーブル例。uidの不可逆圧縮はした方が良いだろうか。した方がよりセキュアではある。

```SQL
CREATE TABLE `users` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `uid` varchar(255) NOT NULL DEFAULT '',
  `nickname` varchar(255) NOT NULL DEFAULT '',
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uid` (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
```

### 4. self-backend -> client ... 認証可否含めたレスポンスの返却

* 3. に成功してればログイン済みユーザとなるし、そうでなければゲスト扱い。通常の会員サービスと変わらない


## その他諸々

### AWS Cognitoは？

Cognitoでも同じことできるだろうけど、用語も実装もfirebaseの方がわかりやすかったので未確認

### 利用料

2018.8現在のところ電話番号認証は1万/月まで無料。それ以降は有料プラン https://firebase.google.com/pricing/?hl=ja
それ以外のAuth機能は無料。
AdminSDK利用とかもしかしたら抜けてるポイントがあるかも？

### instagram や自作認証機構、他のプロバイダーを使う。

カスタム認証を使って作る
https://firebase.google.com/docs/auth/ios/custom-auth
https://developers-jp.googleblog.com/2016/10/authenticate-your-firebase-users-with.html
https://github.com/search?q=firebase+instagram


### firestoreやcloud sqlを使えば

この辺使うとするとgceやcloud functionも必要になってきそうで、S3やTranscoderやもろもろ乗り換える必要が出てきそうで、なかなか手が出ない。心理的ロックイン感。

データにより分けるのは良いかもしれない。ログやマスタデータのみfirestoreとかすると、firebaseの管理画面から閲覧修正できるので便利かも。