サードパーティのストレージ アクセスをブロックするブラウザでリダイレクト ログインの不具合を緩和する

このドキュメントでは、サードパーティの Cookie をブロックするブラウザで、リダイレクト ログインが動作しない状況を緩和する方法について説明します。

概要

Firebase デベロッパーとそのユーザーに対してシームレスな signInWithRedirect() フローを提供するために、Firebase Authentication では、アプリの Firebase Hosting ドメインに接続するクロスオリジンの iframe を使用しています。ただし、このメカニズムは、サードパーティのストレージ アクセスをブロックするブラウザでは機能しません。

ブラウザのストレージ パーティショニング機能を無効にするようユーザーに求めることは現実的ではないため、ユースケースの具体的な仕様に応じて、代わりに以下のいずれかの緩和策をアプリに適用できます。

  • Firebase Hosting を使用して firebaseapp.com のサブドメインでアプリをホストする場合、この問題による影響はなく、特別な対応は必要ありません。
  • Firebase Hosting を使用してカスタム ドメイン(または web.app)でアプリをホストする場合は、緩和策 1 を使用します。
  • Firebase 以外のサービスでアプリをホストする場合は、緩和策 #2、緩和策 #3、緩和策 #4、緩和策 #5 を使用します。

緩和策 1: カスタム ドメインを authDomain として使用するように Firebase 構成を更新する

カスタム ドメインを使用して Firebase Hosting でアプリをホストしている場合は、カスタム ドメインを authDomain として使用するように Firebase SDK を構成できます。これにより、アプリと認証 iframe が同じドメインを使用するようになり、ログインの問題を回避できます(Firebase Hosting を使用しない場合は、別の緩和策を使用する必要があります)。

カスタム ドメインを認証ドメインとして使用するように Firebase 構成を更新する手順は次のとおりです。

  1. カスタム ドメインを authDomain として使用するように Firebase JS SDK を構成します。

    const firebaseConfig = {
      apiKey: "<api-key>",
      authDomain: "<the-domain-that-serves-your-app>",
      databaseURL: "<database-url>",
      projectId: "<project-id>",
      appId: "<app-id>"
    };
    
  2. OAuth プロバイダの承認済みリダイレクト URI のリストに新しい authDomain を追加します。この方法はプロバイダによって異なりますが、一般的な手順は、お使いのプロバイダの「始める前に」セクション(例: Facebook プロバイダ)で確認できます。承認のために更新された URI は https://<the-domain-that-serves-your-app>/__/auth/handler のようになります。末尾の /__/auth/handler が重要です。

緩和策 2: signInWithPopup() に切り替える

signInWithRedirect() ではなく signInWithPopup() を使用します。アプリのコードの残りの部分は同じままですが、UserCredential オブジェクトの取得方法が異なります。

Web version 9

  // Before
  // ==============
  signInWithRedirect(auth, new GoogleAuthProvider());
  // After the page redirects back
  const userCred = await getRedirectResult(auth);

  // After
  // ==============
  const userCred = await signInWithPopup(auth, new GoogleAuthProvider());

Web version 8

  // Before
  // ==============
  firebase.auth().signInWithRedirect(new firebase.auth.GoogleAuthProvider());
  // After the page redirects back
  var userCred = await firebase.auth().getRedirectResult();

  // After
  // ==============
  var userCred = await firebase.auth().signInWithPopup(
      new firebase.auth.GoogleAuthProvider());
```

ポップアップ ログインはユーザーにとって必ずしも理想的なものではありません。ポップアップはデバイスやプラットフォームによってブロックされることがあり、モバイル ユーザーにとってはフローがスムーズではありません。ポップアップの使用がアプリにとって問題となる場合は、他のいずれかの緩和策を採用する必要があります。

緩和策 3: firebaseapp.com へのプロキシ認証リクエスト

signInWithRedirect フローでは、最初にアプリのドメインから Firebase 構成の authDomain パラメータで指定されたドメイン(デフォルトでは .firebaseapp.com)にリダイレクトします。authDomain は、ID プロバイダにリダイレクトするログイン ヘルパーコードをホストします。これが成功すると、再びリダイレクトしてアプリのドメインに戻ります。

認証フローがアプリのドメインに戻ると、ログイン ヘルパー ドメインのブラウザ ストレージへのアクセスが行われます。(コードを自己ホストするための)この緩和策と次の緩和策により、ブラウザによってブロックされるクロスオリジンのストレージ アクセスをなくすことができます。

  1. アプリサーバーにリバース プロキシを設定して、https://<app domain>/__/auth/ への GET/POST リクエストが https://<project>.firebaseapp.com/__/auth/ に転送されるようにします。この転送がブラウザに対して透過的であるようにします。302 リダイレクトを使用してこれを行うことはできません。

    nginx を使用してカスタム ドメインを提供する場合、リバース プロキシ構成は次のようになります。

    # reverse proxy for signin-helpers for popup/redirect sign in.
    location /__/auth {
      proxy_pass https://<project>.firebaseapp.com;
    }
    
  2. 緩和策 1 の手順に沿って、承認済みの redirect_uriauthDomain を更新します。アプリを再デプロイすると、クロスオリジンのストレージ アクセスは行われなくなります。

緩和策 4: ログイン ヘルパー コードを自社ドメイン内でホストする

ヘルパーコードをホストする手順は次のとおりです。

  1. 次のコマンドを実行して、<project>.firebaseapp.com ロケーションからホストするファイルをダウンロードします。

    mkdir signin_helpers/ && cd signin_helpers
    wget https://<project>.firebaseapp.com/__/auth/handler
    wget https://<project>.firebaseapp.com/__/auth/handler.js
    wget https://<project>.firebaseapp.com/__/auth/experiments.js
    wget https://<project>.firebaseapp.com/__/auth/iframe
    wget https://<project>.firebaseapp.com/__/auth/iframe.js
    
  2. 自社のアプリドメインで上記のファイルをホストします。ウェブサーバーが https://<app domain>/__/auth/<filename> に応答できるようにします。

    ファイルをダウンロードしてホストするサーバー実装のサンプルをご確認ください。

  3. 緩和策 1 の手順に沿って、承認済みの redirect_uriauthDomain を更新します。アプリを再デプロイすると、クロスオリジンのストレージ アクセスは行われなくなります。

緩和策 5: プロバイダのログインを独自に処理する

Firebase Authentication SDK には、複雑なロジックをラップし、別の SDK を組み込まなくても済むようにするための便利なメソッドとして、signInWithPopup()signInWithRedirect() が用意されています。これらのメソッドをまったく使用しないという選択肢もあります。その場合は、プロバイダに個別にログインし、signInWithCredential() を使用してプロバイダの認証情報を Firebase Authentication の認証情報と交換します。たとえば、Google ログイン SDKサンプルコード)を使用して Google アカウントの認証情報を取得してから、次のコードを実行して、新しい Google 認証情報をインスタンス化できます。

Web version 9

  // `googleUser` from the onsuccess Google Sign In callback.
  //  googUser = gapi.auth2.getAuthInstance().currentUser.get();
  const credential = GoogleAuthProvider.credential(googleUser.getAuthResponse().id_token);
  const result = await signInWithCredential(auth, credential);

Web version 8

  // `googleUser` from the onsuccess Google Sign In callback.
  const credential = firebase.auth.GoogleAuthProvider.credential(
      googleUser.getAuthResponse().id_token);
  const result = await firebase.auth().signInWithCredential(credential);

signInWithCredential() を呼び出した後のアプリの他の部分は、それまでと同じように機能します。

Apple の認証情報を取得する手順については、こちらをご覧ください。