Firebase
FirebaseAuth

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)の確認

// 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の不可逆圧縮はした方が良いだろうか。した方がよりセキュアではある。
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の管理画面から閲覧修正できるので便利かも。