缓解阻止第三方访问存储空间的浏览器上重定向登录失败的问题

本文档介绍了如何缓解阻止第三方 Cookie 的浏览器上重定向登录失败的问题。

概览

为了使 Firebase 开发者及其用户顺利执行 signInWithRedirect() 流程,Firebase Authentication 会使用连接到您应用的 Firebase Hosting 网域的跨源 iframe。但是,此机制不适用于阻止第三方访问存储空间的浏览器。

由于要求用户停用浏览器上的存储空间分区功能不大现实,因此您可以改为根据您的具体使用情形对您的应用使用以下缓解措施之一。

  • 如果您使用 Firebase Hosting 在 firebaseapp.com 的子网域上托管应用,将不会受到此问题的影响,无需执行任何操作。
  • 如果您使用 Firebase Hosting 在自定义网域(或 web.app)上托管应用,请使用缓解措施 1。
  • 如果您使用 Firebase 以外的服务托管应用,请使用缓解措施 2、缓解措施 3、缓解措施 4 或缓解措施 5。

缓解措施 1:更新 Firebase 配置以使用自定义网域作为 authDomain

如果您使用 Firebase Hosting 在自定义网域上托管应用,则可以配置 Firebase SDK 以使用自定义网域作为 authDomain。这可确保应用和身份验证 iframe 使用相同的网域,从而防止出现登录问题。(如果不使用 Firebase Hosting,则需使用其他缓解措施。)

如需更新 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>"
    };
    
  2. 将新的 authDomain 添加到 OAuth 提供方的已授权重定向 URI 列表中。如何添加将取决于提供方,但一般而言,您可以按照任何提供方(例如 Facebook 提供方)的“准备工作”部分的说明操作。更新后的授权 URI 如下所示:https://<the-domain-that-serves-your-app>/__/auth/handler。末尾的 /__/auth/handler 很重要。

缓解措施 2:切换到 signInWithPopup()

使用 signInWithPopup(),而不要使用 signInWithRedirect()。应用代码的其余部分保持不变,但 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 将托管用于重定向到身份提供方的登录帮助程序代码,成功验证身份后,该代码会重定向回应用网域。

当身份验证流程返回您的应用网域时,系统会访问登录帮助程序网域的浏览器存储空间。此缓解措施和下面的缓解措施(用于自行托管代码)可以避免跨源访问存储空间(浏览器会阻止这种访问)。

  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_uri 和您的 authDomain。重新部署应用后,应该就不会再发生跨源访问存储空间的现象。

缓解措施 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_uri 和您的 authDomain。重新部署应用后,应该就不会再发生跨源访问存储空间的现象。

缓解措施 5:独立处理提供方登录

Firebase Authentication SDK 提供了两种便捷的方法 signInWithPopup()signInWithRedirect() 来封装复杂的逻辑并避免涉及其他 SDK。您也可以不使用这两种方法,而是独立登录到提供方服务,然后通过 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 凭据,请点击此处