Firebase Authenticationを使った実際のシステム設計をどうするか
Firebase Authentication(をつかったシステムの設計)はとにかく難しい
Firebase Authenticationは単純にユーザー認証をするだけなら簡単なのですが、複数の機能を組み合わせて使ったり、自前のサービスの認可機能との整合性を持たせようとすると途端に難しくなります。
この記事のスコープ外ですが、ユーザ情報がFB Authと永続化層の二重管理になりがちでその整合性をどう保つかという点も結構難しいポイントだと思います。
この記事では具体的な実装方法には立ち入らず、Firebase Authenticationに登場する要素と働き、設計の際に参照すべきAPIについてまとめたいと思います。
なお、認証認可の基本的な方法論や、JWTの仕組みについてはすでに理解しているものとして話を進めたいと思います。
Firebase Authentication に登場する要素と役割の紹介
それぞれの役割の違いを理解したうえで、それぞれが協調的に動作するようにロジックを組む必要があります。
処理を行う主体
言うまでもないことですが、Firebase Authetication は PaaSであるため、認証認可処理を行う主体としては、
- クライアント
- サーバ
- Firebaseのバックエンド
の3者があります
SDKとREST APIエンドポイント
Firebase Autheticationでは、SDKライブラリと複数のREST API エンドポイントを提供しています。さらにSDKはブラウザのJavaScriptで動作するクライアントライブラリとバックエンドサーバ上で動作するAdmin SDKに分かれます。
したがって、以下の三つのライブラリとREST APIを使用する必要があります
- クライアントライブラリ
- Admin SDK
- REST API エンドポイント
認証認可用のトークン
Firebase Authenticationで扱うトークンは以下の3種類です
- IDトークン
- リフレッシュトークン
- カスタムトークン
IDトークン
IDトークンは単純に認証用のためのトークンです。ベアラートークンなのでそのトークンの持ち主=Firebase Authenticationに登録したユーザということを証明します。なお有効期間は3600秒(1時間)です。
リフレッシュトークン
リフレッシュトークンは期限切れのIDトークンを再発行するためのトークンです。クライアントSDKのAPIを使用するかREST APIエンドポイントにリクエストすることで新しいIDトークンを発行できます。IDトークンより長い有効期間があります。
カスタムトークン
あなたの開発しているシステムのバックエンドサーバが主体となって発行できるトークンです。(実際は、Admin SDK経由でFirebase Authenticationが発行しているが話を単純化する)
これをすると何がうれしいかというと、Firebaseの他のサービス(Cloud Firestoreなど)での認可に使うカスタムデータをclaimとしてJWT自体に持たせることができます。
ここでの注意点としてカスタムトークンはIDトークンの代わりにはならないという点があります。
つまり、Admin SDKに生えているAPIではカスタムトークンを検証してユーザを認証することができません(verifyCustomToken的なメソッドがない)
また、使用タイミングもIDトークン、リフレッシュトークンはサインイン時に払い出される一方、カスタムトークンはそれ自体がFB Authへのサインインに使用されるという点で異なっています
なお、自前実装でREST APIと公開鍵とJWTのライブラリを使って無理やりバックエンドで検証することは技術的に可能です。これを実施してJWTからclaimを取り出して自分のシステムの認可に活用することもできなくはないです。
かなり紛らわしく混乱しそうになる話ですが、そのような設計になっています。
あくまでもカスタムclaimでFirebaseの他のサービスの認可をするために発行するもの、と覚えておけばいいと思います。
実際の設計
いつどこでどのAPIを使うのか
技術選定フェーズでFirebase Authenticationを認証基盤として採用したとして、どこでどのAPIを使えばいいのかあらかじめ理解していないとまともに設計はできません。以下の表のように整理されると私は理解しています。
なお REST APIは実際のエンドポイントを書くと長くなるので REST APIでまとめました
詳しくはGoogleのドキュメントを参照してください。
-> <- はバックエンド、フロントエンド間のトークンの受け渡しを意味しています。
機能 | トークン | フロントエンド ライブラリ |
バックエンド (Admin SDK) |
---|---|---|---|
認証 | IDトークン | ※signInWithXXX | -> verifyIdToken |
認可(自分のサービス) | なし | なし | なし |
認可(FBの他サービス) | カスタムトークン | ※signInWithCustomToken <- | createCustomToken |
IDトークンの更新 | リフレッシュトークン | ライブラリが自動更新 (JavaScriptでリフレッシュトークンの取得は可能) |
REST API |
※はREST APIを使えばサーバサイドでも実施可能
設計上の選択
これを把握したうえで、開発しているシステムにおいて
- カスタムトークンを活用するか
- ※の処理をサーバサイドに寄せるか寄せないか
例えばサーバサイドにカスタムトークンでのサインイン処理を寄せるか寄せないかで以下の図のようなパターンが考えられます
- 各トークンをどこに保存するのか(クライアントサイドか、サーバサイドか、具体的な保存の仕方など)
- トークンの受け渡しをどうするのか(cookieにするのかリクエストのAuthorizationヘッダーにするのか等)
- 自分のサービスの認可の仕組みとの整合性はどうするか
等の設計上の決定をする必要があります。
このあたりは自分のサービスの事情をもとにコストやUXやセキュリティなどのトレードオフを考慮しながら行うことになると思います。
結論
Firebase Authenticationはエンジニア志望者のポートフォリオ作成に勧められたりしていますが、正直初学者には難しすぎると書いてて思いました。
(今だったらSupabaseとかもありますし)
また挙げた3つのトークン以外に ID プロバイダ トークンというものもありますが今回は余裕がないので割愛します。
一方で設計上の選択で書いたような問題は、海外の会社の面接などでシステムデザインの問題として問われる事柄に近い問題で、かつ現場での経験がものを言う領域です。
これらの問題はセキュリティの問題をはらむので別の要件とのトレードオフを考慮しつつ解決する必要があります。セキュリティの問題は定石はあれど完璧な答えなどありません。これらの問題にきちんと言及することができれば、Webエンジニアとしての見識を示すことができると思います。
Discussion