Posted at

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の管理画面から閲覧修正できるので便利かも。