概要
Webサービスを開発する上で、ユーザー認証は避けては通れない重要な機能です。特に、ユーザーが一度ログインすれば、ブラウザを閉じてもその状態が維持される「ログイン維持」機能や、Googleなどの外部サービスと連携する「ソーシャルログイン」は、今や当たり前のものとなりました。
本記事では、モダンなWebアプリケーション開発で広く採用されている「Firebase Authentication」を例に、これらの認証機能がどのような仕組みで実現されているのか、その裏側にあるセキュリティやセッション管理の考え方について、開発者が知っておくべき点を網羅的に解説します。
ログイン維持の基本メカニズム
Firebaseにおけるログイン維持機能は、IDトークンとリフレッシュトークンという2種類の鍵によって実現されています。これらが連携することで、ユーザーは快適かつ安全にサービスを使い続けることができます。
ログインから状態復元までのフロー
-
初回ログインとトークン発行: ユーザーが認証に成功すると、Firebaseサーバーは2つのトークンを発行します。
- IDトークン: 短期有効(デフォルト1時間)。ユーザー情報を含み、「この人は誰か」を証明する通行証の役割を果たします。
- リフレッシュトークン: 長期有効。IDトークンの有効期限が切れた際に、新しいIDトークンを再発行してもらうための再発行券です。
-
リフレッシュトークンの永続化: Firebase SDKは、この長期有効なリフレッシュトークンをブラウザの
IndexedDBに安全に保存します。IndexedDBはブラウザを閉じてもデータが消えない永続的なストレージです。 -
次回アクセス時の自動復元: ユーザーが再びサイトを訪れると、SDKは
IndexedDBからリフレッシュトークンを読み出します。そして、そのリフレッシュトークンを使い、バックグラウンドでFirebaseサーバーから新しいIDトークンを静かに取得します。このプロセスが成功すると、ユーザーはログイン状態として復元され、onAuthStateChangedリスナーが呼び出されます。
この一連の流れが自動で行われるため、ユーザーはログイン状態が維持されていると感じるのです。
セキュリティと利便性の両立 - 2つのトークンの役割
なぜトークンはわざわざ2種類に分かれているのでしょうか。それは「セキュリティ」と「利便性」という、時に相反する要求を両立させるための優れた設計だからです。
IDトークンはAPIリクエストのたびに送信されるため、漏洩リスクが相対的に高くなります。そのため有効期限を短く設定し、万が一の被害を最小限に抑えます。一方で、リフレッシュトークンはIDトークンの再発行時にしか使われないため、ネットワーク上を流れる頻度が極端に低く、攻撃対象領域(アタックサーフェス)が狭くなります。この安全性を背景に、有効期限を長く設定し、ユーザーが頻繁に再ログインする手間を省いているのです。
さらに、たとえ有効期限内であっても、ユーザーのパスワード変更、明示的なログアウト、アカウントの無効化など、セキュリティに関わるイベントが発生すると、Firebaseサーバー側でリフレッシュトークンは即座に無効化(Revocation)され、安全が保たれます。
セッションのライフサイクル - 始まりから終わりまで
セッションには明確なライフサイクルがあります。IDトークンの有効期限が切れると、有効なリフレッシュトークンを使って新しいIDトークンが自動的に取得され、セッションは継続します。
しかし、そのリフレッシュトークン自体の有効期限が切れた場合、セッションは完全に終了します。SDKは新しいIDトークンを取得できず、onAuthStateChangedリスナーはnullを返します。この状態になると、ユーザーは手動で再ログインする以外に方法はありません。
なお、Firebaseの標準SDKには、リフレッシュトークンの有効期限が近づいた際にそれを自動で更新・延長する仕組みはありません。「リフレッシュトークンが使えなくなったらセッションの終わり」という、シンプルで分かりやすいセキュリティモデルが採用されています。
ログアウト後の再認証 - 新たなセッションの開始
ログアウトは、リフレッシュトークンを無効化し、セッションを完全に終了させる行為です。そのため、再度サービスを利用するには、全く新しいセッションを開始するための「再認証」が必要となります。
再認証に必要な情報は、ユーザーがどの認証方法を利用するかに依存します。User ID (UID)は認証の結果として得られる識別子であり、認証自体に使う情報ではない点に注意が必要です。
認証方法ごとに必要な情報
- メールアドレスとパスワード: ユーザーが登録したメールアドレスとパスワードのペア。
- 電話番号認証: ユーザーの電話番号と、その番号にSMSで送信された確認コード。
- 匿名認証: ユーザーからの入力は不要。SDKが関数を呼び出すだけで一時的なアカウントが作成されます。
ソーシャルログインの裏側
特に、Googleのようなソーシャルログインは、OAuth 2.0とOpenID Connect (OIDC)という標準技術に基づいた、より高度なプロセスです。
1. Googleから返される情報
ユーザーがGoogleで認証し、アプリ連携を許可すると、GoogleはIDトークンをFirebaseに渡します。このトークンはJWT形式で、中には以下のような重要な情報が含まれています。
-
sub(Subject): そのGoogleアカウントの、世界で一意かつ不変のID。 これがユーザーを特定する鍵となります。 -
email,name,picture: プロフィール情報。
このIDトークンは、「この情報は正当なGoogleユーザーのものです」とGoogleが電子署名で保証した身分証明書と言えます。
2. Firebaseによるユーザーの特定と紐付け
Firebaseは、このGoogle IDトークンを受け取ると、以下のロジックで自社のユーザーと紐付けます。
-
初回ログイン時:
- まず、受け取ったGoogle IDトークンの署名を検証し、本物であることを確認します。
- トークン内の
subIDをキーにして、自社のユーザーデータベースを検索します。該当者はいません。 - そこで、新しいFirebaseユーザーを作成し、新しいFirebase UIDを割り当てます。
- この新しいFirebase UIDと、Googleの
subIDを内部で紐付けて保存します。
-
2回目以降のログイン時:
- 同様にGoogle IDトークンを検証し、
subIDを取り出します。 -
subIDをキーにデータベースを検索すると、以前作成された紐付け情報が見つかります。 - これにより、既存のFirebaseユーザーとして特定し、認証します。
- 同様にGoogle IDトークンを検証し、
このプロセスが完了すると、Firebaseは自身のプラットフォームで有効な新しいFirebase IDトークンとリフレッシュトークンを発行し、セッションを開始します。
なお、もし別の方法(例:パスワード認証)で登録された同じメールアドレスのアカウントが既に存在する場合、Firebaseのデフォルト設定では、セキュリティのためにログインが失敗します。この場合、開発者は2つのアカウントを**リンク(統合)**させる処理を実装することが推奨されます。
開発者による制御 - 永続性レベルのカスタマイズ
開発者はsetPersistenceメソッドを使うことで、サービスのセキュリティ要件に応じて、ログイン維持の挙動を3つのレベルから選択できます。
-
browserLocalPersistence(デフォルト): ブラウザを閉じてもログインが維持されます(IndexedDB使用)。 -
browserSessionPersistence: ブラウザのタブ/ウィンドウを閉じるとログアウトします(SessionStorage使用)。 -
inMemoryPersistence: ページをリロードしただけでログアウトします(メモリ使用)。
まとめ
Firebase Authenticationのログイン機能は、単に便利なだけでなく、セキュリティと利便性を高いレベルで両立させるための洗練された仕組みに基づいています。2種類のトークンの役割分担、攻撃対象領域を意識した設計、そして明確なセッションのライフサイクル管理。これらの要素を理解することは、安全で快適なWebサービスを構築する上で不可欠です。本記事が、その理解の一助となれば幸いです。