身份验证状态保留

使用 Firebase JS SDK 时,您可以指定身份验证状态如何持久保留。其中包括指定已登录的用户是在显式退出登录之前无限期地保留身份验证状态、在窗口关闭时清除身份验证状态,还是在页面重新加载时清除身份验证状态。

对于 Web 应用,其默认行为是在用户关闭浏览器之后仍保留该用户的会话。这样,用户不需要每次在同一设备上访问该网页时反复登录,非常方便。否则,就可能需要用户重新输入密码、发送短信验证码等,给用户带来诸多不便。

但是,这种行为可能并不适用于以下这些情况:

  • 涉及敏感数据的应用可能要在窗口或标签页关闭时清除身份验证状态。万一用户忘记退出登录,这种做法就尤为重要。
  • 在由多个用户共用的设备上运行的应用。一个常见的示例就是在图书馆计算机上运行的应用。
  • 在可能由多个用户共用的设备上运行的应用。开发者无法分辨该应用的使用方式,并且可能需要让用户自主选择是否保留自己的会话。这可以通过在登录界面上添加一个“记住我”选项来完成。
  • 在某些情况下,开发者可能希望不保留匿名用户的会话,除非此类用户升级到非匿名账号(联合登录、密码登录、手机登录等)。
  • 开发者可能希望允许不同的用户分别在不同的标签页上登录某个应用。默认的行为是在相同主机源的不同标签页中保留身份验证状态。

如上所述,有多种情况可能需要修改默认的永久保留行为。

支持的 Auth 状态保留类型

共有三种 Auth 状态保留方式,您可以根据应用或用户的要求,为指定的 Firebase Auth 实例选择其中一种。

Enum 说明
firebase.auth.Auth.Persistence.LOCAL 'local' 表示即使浏览器窗口关闭或 activity 在 React Native 中被销毁,仍将保留身份验证状态。需要显式退出登录才能清除该状态。请注意,Firebase Auth 的 Web 会话都是单个主机源,并且只针对单个网域进行保留。
firebase.auth.Auth.Persistence.SESSION 'session' 表示将只在当前会话或标签页中保留身份验证状态,并且在用户进行身份验证的标签页或窗口关闭时会清除该状态。仅适用于 Web 应用。
firebase.auth.Auth.Persistence.NONE 'none' 表示身份验证状态将仅存储在内存中,并且在窗口或 activity 刷新时被清除。

修改 Auth 状态保留类型

您可以通过调用 firebase.auth().setPersistence 方法来指定或修改现有的保留类型:

Web

import { getAuth, setPersistence, signInWithEmailAndPassword, browserSessionPersistence } from "firebase/auth";

const auth = getAuth();
setPersistence(auth, browserSessionPersistence)
  .then(() => {
    // Existing and future Auth states are now persisted in the current
    // session only. Closing the window would clear any existing state even
    // if a user forgets to sign out.
    // ...
    // New sign-in will be persisted with session persistence.
    return signInWithEmailAndPassword(auth, email, password);
  })
  .catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
  });

Web

firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
  .then(() => {
    // Existing and future Auth states are now persisted in the current
    // session only. Closing the window would clear any existing state even
    // if a user forgets to sign out.
    // ...
    // New sign-in will be persisted with session persistence.
    return firebase.auth().signInWithEmailAndPassword(email, password);
  })
  .catch((error) => {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
  });

这将更改指定的 Auth 实例中当前保存的 Auth 会话的保留类型,并将新的保留类型应用于将来的登录请求,包括使用重定向请求进行的登录。这将返回一个 promise 对象,当身份验证状态完成从一种存储方式到另一种存储方式的复制后,就可解析出该对象的值。如果在更改保留类型之后调用登录方法,那么系统将等待该保留类型更改操作完成,然后才会将新的保留类型应用于新的 Auth 状态。

网络浏览器和 React Native 应用默认设置为 local(前提是浏览器支持这种存储机制,例如启用了第三方 Cookie/数据),而 Node.js 后端应用的默认值则为 none

保留行为概览

在确定当前的保留状态时,将采用以下标准。

  • 最开始,SDK 将检查是否存在已通过身份验证的用户。除非调用了 setPersistence,否则该用户当前的保留类型将应用于以后的登录操作。因此,如果该用户在之前某个网页上的保留类型为 session 并访问了新的页面,那么在以其他用户身份再次登录时,会导致新用户的身份验证状态也采用 session 保留类型进行保存。
  • 如果没有已登录的用户,也没有指定保留类型,则将采用默认设置(在浏览器应用中为 local)。
  • 如果没有已登录的用户,并且设置了新的保留类型,则将来的任何登录操作都将使用该保留类型。
  • 如果用户已登录,并且修改了保留类型,则现有登录用户将更改为新的保留类型。所有将来的登录操作都将使用这一新的保留类型。
  • 当调用 signInWithRedirect 时,即使保留类型为 none,也将保留当前的保留类型,并在 OAuth 流结束时将该保留类型应用于新登录的用户。如果该页面明确指定了保留类型,它将覆盖启动了重定向流的前一个页面中的 Auth 状态保留类型。

    Web

    import { getAuth, setPersistence, signInWithRedirect, inMemoryPersistence, GoogleAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    setPersistence(auth, inMemoryPersistence)
      .then(() => {
        const provider = new GoogleAuthProvider();
        // In memory persistence will be applied to the signed in Google user
        // even though the persistence was set to 'none' and a page redirect
        // occurred.
        return signInWithRedirect(auth, provider);
      })
      .catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
      });

    Web

    firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE)
      .then(() => {
        var provider = new firebase.auth.GoogleAuthProvider();
        // In memory persistence will be applied to the signed in Google user
        // even though the persistence was set to 'none' and a page redirect
        // occurred.
        return firebase.auth().signInWithRedirect(provider);
      })
      .catch((error) => {
        // Handle Errors here.
        var errorCode = error.code;
        var errorMessage = error.message;
      });

跨浏览器标签页的预期行为

当不同的标签页中使用了不同的保留类型时,会发生下文介绍的预期行为。要求:在任何时候,不得同时存在多种类型的保存状态(例如,采用 sessionlocal 存储方式保存的 Auth 状态):

  • 在不同用户使用多个标签页的情况下,这些用户可以使用 sessionnone 保留类型登录。每个标签页都看不到其他标签页的身份验证状态。
  • 如果有人尝试使用 local 保留类型进行登录,系统会检测到此行为并将其同步到所有标签页。如果某个用户在此之前已在某个特定标签页上使用 sessionnone 保留类型登录,那么该身份验证状态将被清除。
  • 如果用户之前使用 local 保留类型登录并打开了多个标签页,然后在其中某个标签页中切换为 nonesession 保留类型,那么该标签页的身份验证状态将被修改,该用户将采用 sessionnone 类型来保留身份验证状态,但在其他所有标签页上,该用户将被强制退出登录。