在信赖方端使用 FedCM 实现身份解决方案

信赖方 (RP) 需要完成以下步骤,才能在其网站上启用 FedCM

在信赖方上调用 FedCM API

IdP 的配置和端点可用后,RP 可以调用 navigator.credentials.get() 来请求允许用户使用 IdP 登录 RP。

在调用该 API 之前,您需要确认 FedCM 在用户的浏览器上可用。如需检查 FedCM 是否可用,请将以下代码封装在 FedCM 实现周围:

  if ('IdentityCredential' in window) {
    // If the feature is available, take action
  } else {
    // FedCM is not supported, use a different identity solution
  }

如需允许用户使用 FedCM 在 RP 上登录 IdP,RP 可以调用 navigator.credentials.get()。从 Chrome 136 开始,RP 可以通过在单个 navigator.credentials.get() 调用中指定多个身份提供方的数组来支持多个 IdP,例如:

  const credential = await navigator.credentials.get({
      identity: {
        // Specify the IdP (or multiple IdPs, supported from Chrome 136) this Relying Party supports
        providers: [
        {
              configURL: 'https://accounts.idp-1.example/config.json',
              clientId: '********'
        },
        {
          configURL: 'https://accounts.idp-2.example/config.json',
          clientId: '********'
        }]
      }
    },
  );
  const { token } = credential;
  
  // Get the current IdP's configURL to identify which provider the user is signed in with
  const currentIdpConfigUrl = credential.configURL;
  if (currentIdpConfigUrl === 'https://idp1.example/foo.json') {
    // handle the case where the user signed in with idp1
  } else if (currentIdpConfigUrl === 'https://idp2.example/bar.json') {
    // handle the case where the user signed in with idp2
    }

通过使用 IdP1IdP2 登录,试用多 IdP 功能

上下文属性

借助可选的 context 属性,RP 可以修改 FedCM 对话框界面中的字符串(例如“登录 rp.example…”、“使用 idp.example…”),以适应预定义的身份验证上下文。context 属性可以具有以下值:

  • signin(默认)
  • signup
  • use
图表:说明 FedCM 对话框的界面组件:左上角显示一个图标。该图标的右侧是一个上下文组件,显示“使用 IdP 登录 RP”消息。底部是带有自定义文本和背景颜色的“继续”按钮。
如何将品牌元素应用于 FedCM 对话框

例如,将 context 设置为 use 将生成以下消息:

显示自定义上下文消息的 FedCM 对话框:上下文消息不是“使用 FedCM 登录”,而是“使用”FedCM。
显示自定义上下文消息的 FedCM 对话框。

浏览器会根据账号列表端点的响应中是否存在 approved_clients 来以不同的方式处理注册和登录使用情形。如果用户已订阅 RP,浏览器将不会显示披露声明文本“继续使用...”。
providers 属性接受一个 IdentityProvider 对象数组,这些对象具有以下属性:

提供方属性

providers 属性接受一个包含以下属性的 IdentityProvider 对象数组:

属性 说明
configURL(必需) IdP 配置文件的完整路径。
clientId(必需) 由 IdP 签发的 RP 的客户端标识符。
loginHint(可选) 通过指定账号端点提供的 login_hints 值之一,FedCM 对话框会选择性地显示指定的账号。
domainHint(可选) 通过指定账号端点提供的 domain_hints 值之一,FedCM 对话框会选择性地显示指定的账号。
mode(可选) 指定 FedCM 界面模式的字符串。可以是以下值之一:
  • "active":FedCM 提示必须由用户互动(例如点击按钮)发起。
  • "passive":无需直接用户互动即可启动 FedCM 提示。
如需详细了解主动模式和被动模式之间的区别,请参阅概览页面

注意:Chrome 132 及更高版本支持 mode 参数。
fields(可选) 一个字符串数组,用于指定 RP 需要 IdP 与其分享的用户信息。您可以选择指定以下字段:
  • "name"
  • "username"
  • "email"
  • "tel"
  • "picture"
注意:Chrome 132 及更高版本支持 Fields API。从 Chrome 141 开始,系统支持 "username""tel" 字段。
params(可选) 用于指定其他键值对参数的自定义对象:
  • scope:包含 RP 需要请求的其他权限的字符串值,例如 "drive.readonly calendar.readonly"
  • nonce:一个随机字符串,用于确保响应是针对此特定请求而发出的。防止重放攻击。
  • 其他自定义键值对参数。

注意:Chrome 132 及更高版本支持 params

活动模式

FedCM 支持不同的用户体验模式配置。被动模式是默认模式,开发者无需进行配置。

在有效模式下使用 FedCM:

  1. 检查用户浏览器中的功能可用情况。
  2. 通过临时用户手势(例如点击按钮)调用 API。
  3. mode 参数传递给 API 调用:
  let supportsFedCmMode = false;
  try {
    navigator.credentials.get({
      identity: Object.defineProperty(
        // Check if this Chrome version supports the Mode API.
        {}, 'mode', {
          get: function () { supportsFedCmMode = true; }
        }
      )
    });
  } catch(e) {}

  if (supportsFedCmMode) {
    // The button mode is supported. Call the API with mode property:
    return await navigator.credentials.get({
      identity: {
        providers: [{
          configURL: 'https://idp.example/config.json',
          clientId: '123',
        }],
        // The 'mode' value defines the UX mode of FedCM.
        // - 'active': Must be initiated by user interaction (e.g., clicking a button).
        // - 'passive': Can be initiated without direct user interaction.
        mode: 'active'
      }
    });
  }

不妨通过此演示试用主动模式。

有效模式下的自定义图标

在有效模式下,IdP 可以直接在 client metadata endpoint 响应中包含 RP 的官方徽标图标。RP 必须提前提供其品牌数据。

从跨源 iframe 内调用 FedCM

如果父框架允许,则可以使用 identity-credentials-get 权限政策从跨源 iframe 内调用 FedCM。为此,请按如下方式将 allow="identity-credentials-get" 属性附加到 iframe 代码:

  <iframe src="https://fedcm-cross-origin-iframe.glitch.me" allow="identity-credentials-get"></iframe>

您可以在示例中查看实际运行情况。

(可选)如果父框架想要限制调用 FedCM 的来源,请发送包含允许的来源列表的 Permissions-Policy 标头。

  Permissions-Policy: identity-credentials-get=(self "https://fedcm-cross-origin-iframe.glitch.me")

如需详细了解权限政策的运作方式,请参阅使用权限政策控制浏览器功能

登录提示 API

借助登录提示,RP 可以建议用户应使用哪个账号登录。这有助于重新验证不确定之前使用过哪个账号的用户。

RP 可以通过调用 navigator.credentials.get() 并将 loginHint 属性设置为从账号列表端点提取的 login_hints 值之一,来选择性地显示特定账号,如以下代码示例所示:

  return await navigator.credentials.get({
    identity: {
      providers: [{
        configURL: 'https://idp.example/manifest.json',
        clientId: '123',
        // Accounts endpoint can specify a 'login_hints' array for an account.
        // When RP specifies a 'exampleHint' value, only those accounts will be
        // shown to the user whose 'login_hints' array contains the 'exampleHint'
        // value
        loginHint : 'exampleHint'
      }]
    }
  });

当没有账号与 loginHint 匹配时,FedCM 对话框会显示登录提示,允许用户登录与 RP 请求的提示匹配的 IdP 账号。当用户点按提示时,系统会打开一个弹出式窗口,其中包含配置文件中指定的登录网址。然后,在链接中附加登录提示和网域提示查询参数。

Domain Hint API

RP 可以选择性地仅显示与特定网域相关联的账号。这对于仅限于公司网域的 RP 很有用。

如需仅显示特定网域账号,RP 应使用从账号列表端点提取的 domain_hints 值之一调用 navigator.credentials.get() 并设置 domainHint 属性,如以下代码示例所示:

  return await navigator.credentials.get({
    identity: {
      providers: [{
        configURL: 'https://idp.example/manifest.json',
        clientId: 'abc',
        // Accounts endpoint can specify a 'domain_hints' array for an account.
        // When RP specifies a '@domain.example' value, only those accounts will be
        // shown to the user whose 'domain_hints' array contains the
        // '@domain.example' value
        domainHint : '@domain.example'
      }]
    }
  });

当没有账号与 domainHint 匹配时,FedCM 对话框会显示登录提示,允许用户登录与 RP 请求的提示匹配的 IdP 账号。当用户点按提示时,系统会打开一个弹出式窗口,其中包含配置文件中指定的登录网址。然后,在链接中附加登录提示和网域提示查询参数。

当没有账号与 domainHint 匹配时,登录提示的示例。
当没有账号与 domainHint 相符时,登录提示的示例。

如需了解详情,请查看演示

自定义参数

借助自定义参数功能,RP 可以向 ID 断言端点提供其他键值对参数。借助 Parameters API,RP 可以向 IdP 传递其他参数,以请求对基本登录以外的资源授予权限。在以下情况下,传递其他参数会很有用:

  • RP 需要动态请求 IdP 拥有的其他权限,例如账单邮寄地址或日历访问权限。用户可以通过使用继续操作功能启动的 IdP 控制的界面流程来授权这些权限,然后 IdP 会共享此信息。

若要使用该 API,RP 会将参数作为对象添加到 navigator.credentials.get() 调用中的 params 属性:

  let {token} = await navigator.credentials.get({
    identity: {
      providers: [{
        clientId: '1234',
        configURL: 'https://idp.example/fedcm.json',
        // Key/value pairs that need to be passed from the
        // RP to the IdP but that don't really play any role with
        // the browser.
        params: {
          IDP_SPECIFIC_PARAM: '1',
          foo: 'BAR'
        }
      },
    }
  });

浏览器会自动将此请求转换为向 IdP 发送的 POST 请求,并将参数作为单个网址编码的 JSON 序列化对象:

  // The assertion endpoint is drawn from the config file
  POST /fedcm_assertion_endpoint HTTP/1.1
  Host: idp.example
  Origin: https://rp.example/
  Content-Type: application/x-www-form-urlencoded
  Cookie: 0x23223
  Sec-Fetch-Dest: webidentity

  // params are translated into urlencoded version of `{"IDP_SPECIFIC_PARAM":"1","foo":"bar"}`
  account_id=123&client_id=client1234&params=%22%7B%5C%22IDP_SPECIFIC_PARAM%5C%22%3A1%2C%5C%22foo%5C%22%3A%5C%22BAR%5C%22%7D%22.

如果 RP 需要任何其他权限,IdP 可以提供重定向链接。例如,在 node.js 中:

  if (rpRequestsPermissions) {
    // Response with a URL if the RP requests additional permissions
    return res.json({
      continue_on: '/example-redirect',
    });
  }

字段

RP 可以指定需要 IdP 与其共享的用户信息。这可以包括姓名、电子邮件地址、用户名、电话号码和个人资料照片的任意组合。所请求的信息将包含在 FedCM 对话框的披露界面中。

注册用户会看到一条消息,告知他们如果选择注册,idp.example 会与 rp.example 分享所请求的信息。如果账号端点的响应不包含 RP 请求的字段,则披露声明文本不会包含此字段。IdP 将从 ID 断言端点了解所有请求的字段,并决定是否应收集更多用户权限以继续。

FedCM 对话框,其中包含以下披露声明界面文本:“如需继续,fedcm-idp-demo.localhost 会将您的用户名和电话号码分享给此网站。”。
披露消息:RP 请求 IdP 仅共享用户名和电话号码。

如需使用“字段”功能,RP 应在 navigator.credentials.get() 调用中添加 fields 数组。这些字段可以包含 nameemailtelusernamepicture 等属性。未来,此列表可能会扩展到包含更多值。 包含 fields 的请求如下所示:

   let { token } = await navigator.credentials.get({
    identity: {
      providers: [{
        // RP requests the IdP to share only username and profile picture
        fields: [ 'username', 'picture'],
        clientId: '1234',
        configURL: 'https://idp.example/fedcm.json',
      },
    }
  });

浏览器会自动将其转换为向 ID 断言端点发出的 HTTP 请求,该请求包含 RP 指定的 fields 参数,以及浏览器在 disclosure_shown_for 参数中向用户披露的字段。为了实现向后兼容,如果显示了披露声明文本,且所请求的字段包含所有三个字段'name''email''picture',浏览器也会发送 disclosure_text_shown=true。从 Chrome 141 开始,disclosure_text_shown 的值不再表示披露声明文本是否实际显示给用户。

  POST /id_assertion_endpoint HTTP/1.1
  Host: idp.example
  Origin: https://rp.example/
  Content-Type: application/x-www-form-urlencoded
  Cookie: 0x23223
  Sec-Fetch-Dest: webidentity

  // The RP only requested to share email and picture. The browser will send `disclosure_text_shown=false`, as the 'name' field value is missing
  account_id=123&client_id=client1234&disclosure_text_shown=false&fields=email,picture&disclosure_shown_for=email,picture

如果 fields 是一个空数组,用户代理将跳过披露信息界面。

不显示披露声明界面消息的 FedCM 被动模式对话框。
在被动模式下,披露声明不会显示。在按钮流程中,系统会完全跳过披露声明界面。

即使账号端点的响应不包含与 approved_clients 中的 RP 相匹配的客户端 ID,也是如此。

在这种情况下,发送到 ID 断言端点disclosure_text_shown 在 HTTP 正文中为 false:

  POST /id_assertion_endpoint HTTP/1.1
  Host: idp.example
  Origin: https://rp.example/
  Content-Type: application/x-www-form-urlencoded
  Cookie: 0x23223
  Sec-Fetch-Dest: webidentity

  account_id=123&client_id=client1234&nonce=234234&disclosure_text_shown=false

显示错误消息

有时,IdP 可能因正当理由(例如客户端未经授权或服务器暂时不可用)而无法颁发令牌。如果 IdP 返回“错误”响应,RP 可以捕获该响应,并且 Chrome 可以通过显示浏览器界面来通知用户,其中包含 IdP 提供的错误信息。

一个 FedCM 对话框,显示用户登录尝试失败后的错误消息。该字符串与错误类型相关联。
一个 FedCM 对话框,显示用户登录尝试失败后的错误消息。该字符串与错误类型相关联。
  try {
    const cred = await navigator.credentials.get({
      identity: {
        providers: [
          {
            configURL: 'https://idp.example/manifest.json',
            clientId: '1234',
          },
        ],
      }
    });
  } catch (e) {
    const code = e.code;
    const url = e.url;
  }

在初始身份验证后自动重新验证用户身份

FedCM 自动重新身份验证(简称“自动重新身份验证”)可让用户自动重新进行身份验证。必须满足以下条件才能自动重新验证用户身份:

  • 用户之前已使用 FedCM 完成初始身份验证。 这里的“初始身份验证”是指用户在同一浏览器实例中首次通过点按 FedCM 登录对话框中的“以...身份继续”按钮来创建账号或登录 RP 的网站。
  • 用户只有一个回访账号。如果多个 IdP 存在回访账号,系统不会自动重新验证用户身份。

虽然在用户创建联合账号之前,显式用户体验有助于防止跟踪(这是 FedCM 的主要目标之一),但在用户完成一次该流程后,这种体验会变得不必要地繁琐:在用户授予权限以允许 RP 和 IdP 之间进行通信后,强制用户再次显式确认他们之前已经确认过的内容,并不会带来任何隐私或安全方面的好处。

启用自动重新身份验证后,浏览器会根据您在调用 navigator.credentials.get() 时为 mediation 指定的选项更改其行为。

  const cred = await navigator.credentials.get({
    identity: {
      providers: [{
        configURL: 'https://idp.example/fedcm.json',
        clientId: '1234',
      }],
    },
    mediation: 'optional', // this is the default
  });

  // `isAutoSelected` is `true` if auto-reauthn was performed.
  const isAutoSelected = cred.isAutoSelected;

mediationCredential Management API 中的一个属性,其行为方式与 PasswordCredentialFederatedCredential 的行为方式相同,并且 PublicKeyCredential 也部分支持该属性。该属性接受以下四个值:

  • 'optional'(默认):尽可能自动重新进行身份验证,否则需要中介。建议您在登录页面上选择此选项。
  • 'required':始终需要中介才能继续,例如点击界面上的“继续”按钮。如果您的用户每次需要进行身份验证时都必须明确授予权限,请选择此选项。
  • 'silent':如果可能,自动重新进行身份验证;否则,静默失败,无需中介。我们建议在专用登录页面以外的其他页面上选择此选项,但前提是您希望用户保持登录状态。例如,在配送网站上的商品页面或新闻网站上的文章页面上。
  • 'conditional':用于 WebAuthn,目前不适用于 FedCM。

通过此调用,在以下情况下会发生自动重新身份验证:

  • FedCM 可供使用。例如,用户未在设置中全局停用 FedCM 或针对 RP 停用 FedCM。
  • 用户仅使用一个账号通过 FedCM API 登录此浏览器上的网站。
  • 用户已使用该账号登录 IdP。
  • 自动重新验证未在过去 10 分钟内发生。
  • RP 在上次登录后尚未调用 navigator.credentials.preventSilentAccess()

满足这些条件后,一旦调用 FedCM navigator.credentials.get(),系统就会尝试自动重新验证用户身份。

mediation: optional 时,自动重新验证可能因只有浏览器知道的原因而不可用;RP 可以通过检查 isAutoSelected 属性来检查是否执行了自动重新验证。

这有助于评估 API 性能并相应地改善用户体验。 此外,当该功能不可用时,系统可能会提示用户通过显式用户中介(即包含 mediation: required 的流程)登录。

通过 FedCM 自动重新进行身份验证的用户。

强制执行 preventSilentAccess() 中介

在用户退出后立即自动重新验证用户身份,这会带来非常糟糕的用户体验。因此,FedCM 在自动重新身份验证后会设置 10 分钟的静默期,以防止出现此行为。这意味着,除非用户在 10 分钟内重新登录,否则自动重新验证最多每 10 分钟发生一次。当用户明确退出 RP 时(例如,通过点击退出按钮),RP 应调用 navigator.credentials.preventSilentAccess() 来明确请求浏览器停用自动重新身份验证。

  function signout() {
    navigator.credentials.preventSilentAccess();
    location.href = '/signout';
  }

用户可以在设置中选择停用自动重新验证

用户可以在“设置”菜单中选择停用自动重新验证:

  • 在桌面版 Chrome 中,依次前往 chrome://password-manager/settings > 自动登录。
  • 在 Android 版 Chrome 中,依次打开设置 > 密码管理工具 > 点按右上角的齿轮图标 > 自动登录。

用户可以通过停用相应切换开关来完全选择停用自动重新验证行为。如果用户在 Chrome 实例中登录了 Google 账号并启用了同步功能,此设置会存储并同步到各个设备。

断开 IdP 与 RP 的连接

如果用户之前已通过 FedCM 使用 IdP 登录 RP,浏览器会在本地将该关系记忆为已关联账号的列表。RP 可以通过调用 IdentityCredential.disconnect() 函数来发起断开连接。此函数可从顶级 RP 帧调用。RP 需要传递 configURL、其在 IdP 下使用的 clientIdaccountHint,才能断开与 IdP 的连接。账号提示可以是任意字符串,只要断开连接端点可以识别该账号即可,例如电子邮件地址或用户 ID(不一定与账号列表端点提供的账号 ID 一致):

  // Disconnect an IdP account 'account456' from the RP 'https://idp.com/'. This is invoked on the RP domain.
  IdentityCredential.disconnect({
    configURL: 'https://idp.com/config.json',
    clientId: 'rp123',
    accountHint: 'account456'
  });

IdentityCredential.disconnect() 会返回 Promise。此 promise 可能会因以下原因抛出异常:

  • 用户尚未通过 FedCM 使用 IdP 登录 RP。
  • 该 API 是从没有 FedCM 权限政策的 iframe 中调用的。
  • config网址 无效或缺少断开连接端点。
  • 内容安全政策 (CSP) 检查失败。
  • 有待处理的断开连接请求。
  • 用户已在浏览器设置中停用 FedCM。

IdP 的断开连接端点返回响应时,RP 和 IdP 会在浏览器上断开连接,并且 Promise 会得到解析。已断开连接的账号的 ID 在断开连接端点的响应中指定。

后续步骤

了解如何在身份提供方端使用 FedCM 实现身份解决方案。
了解用户和开发者如何管理 FedCM 参与情况,包括在各个平台和网站上选择启用或停用。