依赖方联合登录

本文介绍了依赖方 (RP) 如何使用联合凭据管理 (FedCM) API通过身份提供商 (IdP) 执行联合登录的过程。

调用 get() 方法

RP 可以使用identity选项调用navigator.credentials.get(),以请求用户使用他们已在浏览器中登录的现有 IdP 帐户登录 RP。IdP 通过其clientId识别 RP,该 ID 由 IdP 在特定于 IdP 的单独过程中颁发给 RP。IdP 使用登录时提供给浏览器的凭据(cookie)识别特定用户。

如果 IdP 成功验证了用户身份,则该方法将返回一个以IdentityCredential对象完成的 Promise。此对象包含一个令牌,其中包含已使用 IdP 的数字证书签名的用户信息。

RP 将令牌发送到其服务器以验证证书,如果成功,则可以使用令牌中(现已可信的)身份信息将其登录到其服务(启动新会话),如果他们是新用户,则将其注册到其服务等。

如果用户从未登录到 IdP 或已注销,则get()方法将拒绝并出现错误,RP 可以将用户引导到 IdP 登录页面以登录或创建帐户。

注意:验证令牌的确切结构和内容对 FedCM API 和浏览器来说是不透明的。IdP 决定其语法和用法,RP 需要遵循 IdP 提供的说明(例如,参见在服务器端验证 Google ID 令牌)以确保他们正确使用它。

一个典型的请求可能如下所示

js
async function signIn() {
  const identityCredential = await navigator.credentials.get({
    identity: {
      context: "signup",
      providers: [
        {
          configURL: "https://accounts.idp.example/config.json",
          clientId: "********",
          nonce: "******",
          loginHint: "[email protected]",
        },
      ],
    },
  });
}

identity.providers属性采用一个数组,其中包含一个对象,该对象指定 IdP 配置文件(configURL)的路径和 IdP 颁发给 RP 的 RP 客户端标识符(clientId)。

注意:目前,FedCM 仅允许使用单个 IdP 调用 API,即identity.providers数组的长度必须为 1。为了向用户提供身份提供商选择,RP 需要分别对每个提供商调用get()。这在将来可能会发生变化。

上面的示例还包含一些可选功能

  • identity.context指定用户使用 FedCM 进行身份验证的上下文。例如,这是此帐户的首次注册,还是使用现有帐户登录?浏览器使用此信息来更改其 FedCM UI 中的文本,使其更适合上下文。
  • nonce属性提供一个随机的 nonce 值,以确保响应是针对此特定请求发出的,从而防止重放攻击
  • loginHint属性提供有关浏览器应显示的用户登录帐户选项的提示。此提示与 IdP 从帐户列表端点提供的login_hints值匹配。

浏览器请求 IdP 配置文件并执行下面详细介绍的登录流程。有关用户可能从浏览器提供的 UI 中获得的交互类型的更多信息,请参阅使用身份提供商登录依赖方

FedCM 登录流程

登录流程涉及三个参与方——RP 应用程序、浏览器本身和 IdP。下图总结了直观上发生了什么。

a visual representation of the flow described in detail below

流程如下

  1. RP 调用navigator.credentials.get()以启动登录流程。
  2. get()调用中提供的configURL,浏览器请求两个文件
    1. 知名文件(/.well-known/web-identity),可在configURLeTLD+1处的/.well-known/web-identity获得。
    2. IdP 配置文件/config.json),可在configURL处获得。
    这两个都是GET请求,它们没有 cookie 并且不遵循重定向。这有效地防止了 IdP 了解谁发出了请求以及哪个 RP 正在尝试连接。通过 FedCM 从浏览器发送的所有请求都包含一个Sec-Fetch-Dest: webidentity标头以防止CSRF攻击。所有 IdP 端点都必须确认此标头已包含。
  3. IdP 响应请求的知名文件和config.json文件。浏览器根据知名文件中有效配置 URL 列表验证get()请求中的配置文件 URL。
  4. 如果浏览器将IdP 的登录状态设置为"logged-in",则它会对 IdP 配置文件中用户帐户详细信息的accounts_endpoint发出带凭据的请求(即,使用识别已登录用户的 cookie)。这是一个带有 cookie 但没有client_id参数或Origin标头的GET请求。这有效地防止了 IdP 了解用户尝试登录到哪个 RP。因此,返回的帐户列表与 RP 无关。

    注意:如果 IdP 登录状态为"logged-out",则get()调用将拒绝并出现NetworkErrorDOMException,并且不会对 IdP 的accounts_endpoint发出请求。在这种情况下,由开发人员处理流程,例如提示用户转到并登录到合适的 IdP。请注意,拒绝可能会有延迟,以避免将 IdP 登录状态泄露给 RP。

  5. IdP 响应从accounts_endpoint请求的帐户信息。这是与用户 IdP cookie 关联的所有帐户(对于与 IdP 关联的任何 RP)的数组。
  6. 可选如果包含在 IdP 配置文件中,浏览器将对client_metadata_endpoint发出无凭据的请求,以获取 IdP 服务条款和隐私政策页面的位置。这是一个以get()调用中作为参数传递的clientId发送但没有 cookie 的GET请求。
  7. 可选IdP 响应从client_metadata_endpoint请求的 URL。
  8. 浏览器使用先前两个请求获得的信息创建 UI,要求用户选择一个帐户以使用该帐户登录 RP(在有多个可用帐户的情况下)。UI 还要求用户允许使用他们选择的联合 IdP 帐户登录 RP。

    注意:在此阶段,如果用户先前已在当前浏览器实例中使用联合 RP 帐户进行了身份验证(即,使用 RP 创建了一个新帐户或使用现有帐户登录到 RP 的网站),他们可能会自动重新验证,具体取决于get()调用中mediation选项的设置。如果是这样,用户将在调用get()后立即自动登录,无需输入其凭据。有关更多详细信息,请参阅自动重新验证部分。

  9. 如果用户授予许可,浏览器将对id_assertion_endpoint发出带凭据的请求,以从 IdP 请求所选帐户的验证令牌。凭据以 HTTPPOST请求发送,其中包含 cookie 和application/x-www-form-urlencoded的内容类型。如果调用失败,则会返回错误有效负载,如ID 断言错误响应中所述,并且get()返回的 Promise 将拒绝并出现错误。
  10. IdP 检查 RP 发送的帐户 ID 是否与已登录帐户的 ID 匹配,以及Origin是否与 RP 的来源匹配,RP 的来源将已预先在 IdP 中注册。如果一切正常,它将响应请求的验证令牌。

    注意:当 RP 首次与 IdP 集成时,RP 的来源将在完全独立的过程中与 IdP 注册。此过程将特定于每个 IdP。

  11. 当流程完成后,get() promise 会解析为一个 IdentityCredential 对象,该对象提供了进一步的 RP 功能。最值得注意的是,此对象包含一个令牌,RP 可以使用证书验证该令牌来自 IdP,并且该令牌包含有关已登录用户的可信信息。一旦 RP 验证了令牌,他们就可以使用包含的信息登录用户并开始新会话,将其注册到其服务等。令牌的格式和结构取决于 IdP,与 FedCM API 无关(RP 需要遵循 IdP 的说明)。

自动重新验证

FedCM 自动重新认证允许用户在使用 FedCM 初次认证后再次尝试登录 RP 时自动重新认证。“初始认证”是指用户在 RP 网站上首次通过 FedCM 登录对话框创建帐户或登录 RP 网站时(在同一浏览器实例中)。

初始认证后,可以使用自动重新认证再次自动登录 RP 网站,而无需向用户显示“继续使用...”确认提示。如果用户最近已授予权限以允许使用特定帐户进行联合登录,则立即强制执行另一个明确的用户确认没有隐私或安全益处。

自动重新认证行为由 get() 调用中的 mediation 选项控制。

js
async function signIn() {
  const identityCredential = await navigator.credentials.get({
    identity: {
      providers: [
        {
          configURL: "https://accounts.idp.example/config.json",
          clientId: "********",
        },
      ],
    },
    mediation: "optional", // this is the default
  });

  // isAutoSelected is true if auto-reauthentication occurred.
  const isAutoSelected = identityCredential.isAutoSelected;
}

如果将 mediation 设置为 optionalsilent,则可能会发生自动重新认证。

使用这些 mediation 选项,自动重新认证将在以下条件下发生。

  • FedCM 可供使用。例如,用户尚未全局禁用 FedCM 或在 RP 的设置中禁用 FedCM。
  • 用户仅使用一个帐户通过 FedCM 登录此浏览器上的 RP 网站。
  • 用户已使用该帐户登录 IdP。
  • 在过去 10 分钟内未发生自动重新认证。此限制是为了防止用户在注销后立即自动重新认证,这会导致用户体验非常混乱。
  • RP 在上次登录后未调用 preventSilentAccess()。RP 可以根据需要使用此方法显式禁用自动重新认证。

满足这些条件后,一旦调用 get(),就会尝试自动重新认证用户。如果自动重新认证成功,用户将再次登录 RP 网站,而不会显示确认提示,并使用与之前相同的 IdP 帐户和已验证令牌。

如果自动重新认证失败,则行为取决于所选的 mediation 值。

  • optional:将向用户显示对话框并再次请求确认。因此,此选项往往适用于用户旅程不在流程中的页面,例如 RP 登录页面。
  • silentget() promise 将被拒绝,开发人员需要处理引导用户返回登录页面以重新开始流程。此选项适用于用户旅程正在进行且您需要将其保持登录状态直到完成的页面,例如电子商务网站的结账流程页面。

注意:IdentityCredential.isAutoSelected 属性指示联合登录是否使用自动重新认证执行。这有助于评估 API 性能并相应地改进用户体验。此外,当它不可用时,可能会提示用户使用显式用户中介登录,即 mediation: requiredget() 调用。

另请参阅