在阻止第三方存储空间访问的浏览器上使用 signInWithRedirect 的最佳做法
本文档介绍了在阻止第三方 Cookie 的浏览器上使用重定向登录的最佳实践。
概览
为了使您和您的用户顺利执行 signInWithRedirect()
流程,Firebase Authentication JavaScript SDK 会使用连接到应用的 Firebase Hosting 网域的跨源 iframe。
但是,此机制不适用于阻止第三方访问存储空间的浏览器。
由于让用户不太可能停用浏览器上的存储分区功能,因此您不太可能选择该选项,而应根据具体用例,将以下设置选项应用到您的应用。
- 如果您使用 Firebase Hosting 在
firebaseapp.com
的子网域上托管应用,将不会受到此问题的影响,无需执行任何操作。 - 如果您在自定义网域或
web.app
的子网域上使用 Firebase Hosting 托管应用,请使用方法 1。 - 如果您使用 Firebase 以外的服务托管应用,请使用选项 2、选项 3、选项 4 或选项 5。
方法 1:更新您的 Firebase 配置,以将您的自定义网域用作 authDomain
如果您使用 Firebase Hosting 在自定义网域上托管应用,则可以配置 Firebase SDK 以使用自定义网域作为 authDomain
。这可确保应用和身份验证 iframe 使用相同的网域,从而防止出现登录问题。(如果您不使用 Firebase Hosting,则需要使用其他选项。)
如需更新 Firebase 配置以使用自定义网域作为身份验证网域,请执行以下操作:
配置 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>" };
将新的
authDomain
添加到 OAuth 提供方的已授权重定向 URI 列表中。如何添加将取决于提供方,但一般而言,您可以按照任何提供方(例如 Facebook 提供方)的“准备工作”部分的说明操作。更新后的授权 URI 如下所示:https://<the-domain-that-serves-your-app>/__/auth/handler
。末尾的/__/auth/handler
很重要。同样,如果您使用的是 SAML 提供方,请将新的
authDomain
添加到 SAML 断言消费者服务 (ACS) 网址。
方法 2:切换到 signInWithPopup()
使用 signInWithPopup()
,而不要使用 signInWithRedirect()
。应用代码的其余部分保持不变,但 UserCredential 对象的检索方式不同。
Web 版本 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 版本 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
参数中指定的网域(默认为“authDomain
将托管用于重定向到身份提供方的登录帮助程序代码,成功验证身份后,该代码会重定向回应用网域。
当身份验证流程返回您的应用网域时,系统会访问登录帮助程序网域的浏览器存储空间。此选项和后面的选项(用于自行托管代码)消除了跨源存储访问,否则浏览器会阻止访问。
在应用服务器上设置反向代理,以便将发送到
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; }
按照选项 1 中的步骤更新已获授权的
redirect_uri
、ACS 网址和authDomain
。重新部署应用后,应该就不会再发生跨源访问存储空间的现象。
方法 4:在网域中自行托管登录帮助程序代码
另一种消除跨源访问存储空间的方法是自行托管 Firebase 登录帮助程序代码。但是,此方法不适用于 Apple 登录或 SAML。仅当选项 3 中的反向代理设置不可行时,才使用此选项。
托管帮助程序代码的步骤如下:
执行以下命令,从
<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
将上述文件托管在应用网域下。确保您的网络服务器可以响应
https://<app domain>/__/auth/<filename>
。此处提供了一个下载和托管文件的服务器实现示例。
按照方法 1 中的步骤更新已获授权的
redirect_uri
和您的authDomain
。重新部署应用后,应该就不会再发生跨源访问存储空间的现象。
选项 5:单独处理提供方登录
Firebase Authentication SDK 提供了两种便捷的方法 signInWithPopup()
和 signInWithRedirect()
来封装复杂的逻辑并避免涉及其他 SDK。您也可以不使用这两种方法,而是独立登录到提供方服务,然后通过 signInWithCredential()
使用提供方的凭据换取 Firebase Authentication 凭据。例如,您可以运行以下代码,使用 Google 登录 SDK、示例代码来获取 Google 帐号凭据,然后将新的 Google 凭据实例化:
Web 版本 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 版本 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 凭据,请点击此处。