在封鎖第三方儲存空間存取權的瀏覽器上使用 signInWithRedirect 的最佳做法

本文件說明在封鎖第三方 Cookie 的瀏覽器上使用重新導向登入功能的最佳做法。您必須採取下列其中一種做法,才能讓 signInWithRedirect() 在實際環境中透過所有瀏覽器正常運作。

總覽

為了讓您和使用者都能順利完成 signInWithRedirect() 流程,Firebase 驗證 JavaScript SDK 會使用跨來源 iframe,連結至應用程式的 Firebase Hosting 網域。不過,這個機制不適用於封鎖第三方儲存空間存取權的瀏覽器。

由於要求使用者在瀏覽器上停用儲存空間分割功能幾乎不是選項,因此您應根據用途的具體情況,在應用程式中套用下列其中一種設定選項。

  • 如果您使用 Firebase 代管服務在 firebaseapp.com 的子網域上代管應用程式,就不會受到這項問題的影響,也不需要採取任何行動。
  • 如果您使用 Firebase 代管服務在自訂網域或 web.app 的子網域上代管應用程式,請使用選項 1
  • 如果您使用 Firebase 以外的服務代管應用程式,請使用選項 2選項 3選項 4選項 5

選項 1:更新 Firebase 設定,以便將自訂網域用於 authDomain

如果您使用自訂網域透過 Firebase 代管代管應用程式,可以設定 Firebase SDK 使用自訂網域做為 authDomain。這可確保應用程式和驗證 iframe 使用相同的網域,避免發生登入問題。(如果您未使用 Firebase 託管,則必須使用其他選項)。請確認您已在用於驗證的專案中設定自訂網域。

如要更新 Firebase 設定,以便將自訂網域用作驗證網域,請按照下列步驟操作:

  1. 設定 Firebase JS SDK,以便使用自訂網域做為 authDomain

    const firebaseConfig = {
      apiKey: "<api-key>",
      authDomain: "<the-domain-that-serves-your-app>",
      databaseURL: "<database-url>",
      projectId: "<project-id>",
      appId: "<app-id>"
    };
    
  1. 將新的 authDomain 新增至 OAuth 供應商的授權重新導向 URI 清單。具體做法取決於供應商,但一般來說,您可以按照任何供應商的「事前準備」一節,瞭解詳細操作說明 (例如 Facebook 供應商)。更新後的授權 URI 會顯示為 https://<the-domain-that-serves-your-app>/__/auth/handler,其中結尾的 /__/auth/handler 很重要。

    同樣地,如果您使用的是 SAML 供應器,請將新的 authDomain 新增至 SAML 斷言消費者服務 (ACS) 網址。

  2. 請確認您的 continue_uri 位於授權網域清單中。

  3. 如有需要,請使用 Firebase 託管重新部署,以便擷取託管在 /__/firebase/init.json 的最新 Firebase 設定檔。

方法 2:改用 signInWithPopup()

請使用 signInWithPopup(),而非 signInWithRedirect()。應用程式的其他程式碼保持不變,但 UserCredential 物件的擷取方式有所不同。

網頁版 9

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

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

網頁版 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:將 Proxy 驗證要求傳送至 firebaseapp.com

signInWithRedirect 流程會先從應用程式網域重新導向至 Firebase 設定中 authDomain 參數指定的網域 (預設為「.firebaseapp.com」)。authDomain 會代管登入輔助程式程式碼,將使用者重新導向至 Identity Provider,而 Identity Provider 會在成功登入後,將使用者重新導向至應用程式網域。

驗證流程返回應用程式網域時,系統會存取登入輔助程式網域的瀏覽器儲存空間。這個選項和下一個選項 (用於自行代管程式碼) 可消除跨來源儲存空間存取權,否則瀏覽器會封鎖這項權限。

  1. 請在應用程式伺服器上設定反向 Proxy,以便將對 https://<app domain>/__/auth/ 的 GET/POST 要求轉送至 https://<project>.firebaseapp.com/__/auth/。請確認這項轉送對瀏覽器是透明的,這無法透過 302 Redirect 完成。

    如果您使用 nginx 提供自訂網域,反向 Proxy 設定會如下所示:

    # reverse proxy for signin-helpers for popup/redirect sign in.
    location /__/auth {
      proxy_pass https://<project>.firebaseapp.com;
    }
    
  2. 請按照選項 1 中的步驟更新授權 redirect_uri、ACS 網址和 authDomain。重新部署應用程式後,跨來源儲存空間存取權就不會再發生。

方法 4:在您的網域中自行代管登入輔助程式程式碼

另一種避免跨來源儲存空間存取權的方法,是自行代管 Firebase 登入輔助程式碼。不過,這種做法不適用於 Apple 登入或 SAML。只有在無法使用選項 3 的反向 Proxy 設定時,才使用這個選項。

代管輔助程式碼的步驟如下:

  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
    wget https://<project>.firebaseapp.com/__/firebase/init.json
    
  2. 在應用程式網域下方代管上述檔案。請確認網路伺服器可回應 https://<app domain>/__/auth/<filename>https://<app domain>/__/firebase/init.json

    以下是下載及代管檔案的伺服器範例實作。建議您定期下載及同步檔案,確保能取得最新的錯誤修正和功能。

  3. 請按照「選項 1」中的步驟更新授權 redirect_uriauthDomain。重新部署應用程式後,跨來源儲存空間存取權就不會再發生。

選項 5:獨立處理供應商登入作業

Firebase 驗證 SDK 提供 signInWithPopup()signInWithRedirect() 做為方便方法,用於包裝複雜的邏輯,避免需要使用其他 SDK。您可以完全避免使用這兩種方法,方法是獨立登入提供者,然後使用 signInWithCredential() 交換提供者的憑證,以取得 Firebase 驗證憑證。舉例來說,您可以使用 Google 登入 SDK程式碼範例取得 Google 帳戶憑證,然後執行下列程式碼,將新的 Google 憑證例項化:

網頁版 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);

網頁版 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 憑證,請參閱本文的操作說明。