FedCM 更新:针对 Continuation API 软件包和 Storage Access API 自动授权的源试用

从 Chrome 126 开始,开发者可以开始针对一系列 通过桌面设备 Federated Credential Management API (FedCM) 功能, 授权用例。该软件包由 Continuation API 和 Parameters API,可提供类似 OAuth 授权流程的体验 其中包含身份提供方 (IdP) 提供的权限对话框。该套装还 包括其他更改,例如 Fields API、多个 config网址 和自定义 账号标签。从 Chrome 126 开始,我们还为 Storage Access API (SAA),用于自动授予 SAA 请求,前提是用户已 曾使用 FedCM 成功登录。

源试用:FedCM Continuation API 软件包

FedCM Continuation API 包包含多个 FedCM 扩展程序:

Continuation API

<ph type="x-smartling-placeholder">
</ph>
用户正在登录 RP,然后通过按钮模式进行授权。

您可以查看 Glitch 上的 API 演示

借助 Continuation API, IdP 的 ID 断言 端点 (可选)返回 FedCM 呈现的网址,以便用户继续 多步骤登录流程。这样,IdP 就可以请求用户授予 现有 FedCM 界面中无法实现的信赖方 (RP) 权限; 例如对用户服务器端资源的访问权限

通常,ID 断言端点会返回 身份验证。

{
  "token": "***********"
}

但是,使用 Continuation API 时,ID 断言 端点可以 会返回 continue_on 属性,其中包含绝对路径或相对路径 ID 断言端点的路径。

{
  // In the id_assertion_endpoint, instead of returning a typical
  // "token" response, the IdP decides that it needs the user to
  // continue on a pop-up window:
  "continue_on": "/oauth/authorize?scope=..."
}

浏览器一旦收到 continue_on 响应,即会显示一个新的弹出式窗口 会打开并将用户引导至指定路径。

用户与页面互动(例如授予进一步权限)后 与 RP 共享额外信息时,IdP 页面可以调用 IdentityProvider.resolve() 以解决原始问题 navigator.credentials.get() 调用并返回词元作为参数。

document.getElementById('allow_btn').addEventListener('click', async () => {
  let accessToken = await fetch('/generate_access_token.cgi');
  // Closes the window and resolves the promise (that is still hanging
  // in the relying party's renderer) with the value that is passed.
  IdentityProvider.resolve(accessToken);
});

然后,浏览器会自行关闭弹出式窗口,并将令牌返回给 API 调用方。

如果用户拒绝请求,您可以通过调用 IdentityProvider.close()

IdentityProvider.close();

如果用户出于某种原因在弹出式窗口中更改了自己的账号(例如, IdP 提供“切换用户”还是在委托的情况下) 调用带有可选的第二个参数,该参数支持如下内容:

IdentityProvider.resolve(token, {accountId: '1234');

参数 API

Parameters API 允许 RP 为 ID 断言提供附加参数 端点。 借助 Parameters API,RP 可以将其他参数传递给 IdP, 请求对基本登录以外的资源的权限。该用户将授权 通过 Continuation API

要使用该 API,请将参数作为对象添加到 params 属性中 navigator.credentials.get() 通话。

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',
        ETC: 'MOAR',
        scope: 'calendar.readonly photos.write',
      }
    },
  }
});

params 对象中的属性名称以 param_ 作为前缀。在 在上面的示例中,params 属性包含 IDP_SPECIFIC_PARAM,分别是 '1'foo'BAR'ETC'MOAR'scope'calendar.readonly photos.write'。 这将翻译为 param_IDP_SPECIFIC_PARAM=1&param_foo=BAR&param_ETC=MOAR&param_scope=calendar.readonly%20photos.write

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

account_id=123&client_id=client1234&nonce=234234&disclosure_text_shown=false&param_IDP_SPECIFIC_PARAM=1&param_foo=BAR&param_ETC=MOAR&param_scope=calendar.readonly%20photos.write

动态获取权限

一般来说,如果用户 而不是在开发者认为它们最容易实现的时候。对于 例如,在用户即将拍照时请求相机的使用权限 最好在用户进入应用页面时就请求权限 网站。相同的做法也适用于服务器资源。仅请求权限 在用户需要时提供这称为“动态授权”。

要通过 FedCM 动态请求授权,IdP 可以执行以下操作:

  1. 使用 IdP 可以处理的必需参数调用 navigator.credentials.get() 例如 scope
  2. ID 断言 端点 确认用户已登录,并返回 continue_on 网址。
  3. 浏览器会打开一个弹出式窗口,其中会显示 IdP 的权限页面 与所请求范围匹配的额外权限。
  4. IdP 通过 IdentityProvider.resolve() 授权后,此窗口会变为 并且 RP 的原始 navigator.credentials.get() 调用会得到一个 相关令牌或授权代码,以便 RP 可以将其与 适当的访问令牌

字段 API

Fields API 允许 RP 执行以下操作: 声明从 IdP 请求的账号属性,以便浏览器可以 在 FedCM 对话框中呈现适当的披露界面;IdP 的职责是 将所请求的字段包含在返回的令牌中。考虑此请求 “基本个人资料”OpenID Connect 与“作用域”。

<ph type="x-smartling-placeholder">
</ph> 微件模式下的披露消息。
微件模式下的披露消息。
。 <ph type="x-smartling-placeholder">
</ph> 按钮模式下的披露消息。
按钮模式下的披露信息。

如需使用 Fields API,请将参数作为数组添加到 fields 属性中 navigator.credentials.get() 通话。这些字段可以包含 'name''email''picture',但可以进行扩展,在 。

带有 fields 的请求如下所示:

let { token } = await navigator.credentials.get({
  identity: {
    providers: [{
      fields: ['name', 'email', 'picture'],
      clientId: '1234',
      configURL: 'https://idp.example/fedcm.json',
      params: {
        scope: 'drive.readonly calendar.readonly',
      }
    },
  }
  mediation: 'optional',
});

ID 断言的 HTTP 请求 端点 包含 RP 指定的 fields 参数,其中 disclosure_text_shown 参数设为 true(如果该用户不是回访用户),以及 disclosure_shown_for 参数中向用户披露的浏览器:

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=true&fields=email,name,picture&disclosure_shown_for=email,name,picture

如果 RP 需要从 IdP 访问任何其他数据(例如访问 calendar 事件,应使用上文提到的自定义参数进行处理。通过 IdP 会返回 continue_on 网址以请求权限。

如果 fields 是空数组,请求将如下所示:

let { token } = await navigator.credentials.get({
  identity: {
    providers: [{
      fields: [],
      clientId: '1234',
      configURL: 'https://idp.example/fedcm.json',
      params: {
        scope: 'drive.readonly calendar.readonly',
      }
    },
  }
  mediation: 'optional',
});

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

<ph type="x-smartling-placeholder">
</ph> 在微件模式下不显示披露消息。在按钮流程中,系统会完全跳过披露界面。
在微件模式下不显示披露声明消息。在按钮流程中,系统会完全跳过披露界面。

即使来自账号 端点 不包含与 approved_clients 中的 RP 匹配的客户端 ID。

在这种情况下,disclosure_text_shown 会发送到 ID 断言 端点为 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

多个 config网址

多个 config网址 允许使用 IdP 来容纳 IdP 的多个配置文件,方法是指定 accounts_endpointlogin_url 文件 用作配置文件

如果将 accounts_endpointlogin_url 添加到已知文件中, provider_urls 会被忽略,以便 IdP 可以支持多个配置文件。 否则,provider_urls 会继续生效,使其向后兼容 兼容。

支持多个 config网址 的知名文件可能如下所示:

{
  "provider_urls": [ "https://idp.example/fedcm.json" ],
  "accounts_endpoint": "https://idp.example/accounts",
  "login_url": "https://idp.example/login"
}

这使我们能够:

  1. 保持与现有已知文件的向后和向前兼容性 以及已实际部署的早期版本浏览器。
  2. 拥有任意数量的配置文件 - 只要它们都指向 相同的 accounts_endpointlogin_url
  3. 没有机会将熵添加到基于凭据的提取请求 对 accounts_endpoint 进行了修改,因为必须在 “.well-known”。

支持多个 config网址 是可选的,支持现有的 FedCM 实现方式可以保持不变

自定义账号标签

自定义账号标签允许 FedCM 为账号添加注释的 IdP,以便 RP 可以通过在 一个配置文件使用域提示 API登录 Hint API: 它们在 navigator.credentials.get() 调用中被传递,而自定义账号标签 可以通过指定配置文件来过滤用户 使用了多个 config网址。自定义账号标签现为 还有一些不同之处在于它们是通过 IdP 服务器(而非从 IdP)提供的 例如登录或网域提示

示例

IdP 分别支持为个人用户和企业用户两个 config网址。通过 消费者配置文件具有 'consumer' 标签,而企业配置文件 具有 'enterprise' 标签。

对于这样的设置,已知文件包含 accounts_endpointlogin_url,以允许使用多个 config网址。

{
  "provider_urls": [ "https://idp.example/fedcm.json" ],
  "accounts_endpoint": "https://idp.example/accounts",
  "login_url": "https://idp.example/login"
}

如果在已知文件中提供了 accounts_endpoint, 系统会忽略 provider_urls。RP 可以直接指向相应的配置 navigator.credentials.get() 调用中的文件。

使用方配置文件位于 https://idp.example/fedcm.json,其中包括 accounts 属性,用于使用 include 指定 'consumer'

{
  "accounts_endpoint": "https://idp.example/accounts",
  "client_metadata_endpoint": "/client_metadata",
  "login_url": "https://idp.example/login",
  "id_assertion_endpoint": "/assertion",
  "accounts": {
    "include": "consumer"
  }
}

企业配置文件位于 https://idp.example/enterprise/fedcm.json, 它包含 accounts 属性,该属性使用'enterprise' include

{
  "accounts_endpoint": "https://idp.example/accounts",
  "client_metadata_endpoint": "/enterprise/client_metadata",
  "login_url": "https://idp.example/login",
  "id_assertion_endpoint": "/assertion",
  "accounts": {
    "include": "enterprise"
  }
}

常用 IdP 账号 端点 (在此示例中为 https://idp.example/accounts)会返回 包含一个标签属性,并在数组中为每个账号分配有 labels。 以下是针对拥有两个账号的用户的响应示例。一个是 另一个适用于企业客户:

{
 "accounts": [{
   "id": "123",
   "given_name": "John",
   "name": "John Doe",
   "email": "john_doe@idp.example",
   "picture": "https://idp.example/profile/123",
   "labels": ["consumer"]
  }], [{
   "id": "4567",
   "given_name": "Jane",
   "name": "Jane Doe",
   "email": "jane_doe@idp.example",
   "picture": "https://idp.example/profile/4567",
   "labels": ["enterprise"]
  }]
}

当 RP 想要允许 'enterprise' 用户登录时,可以指定 'enterprise' 配置网址 'https://idp.example/enterprise/fedcm.json' 位于 navigator.credentials.get()调用:

let { token } = await navigator.credentials.get({
  identity: {
    providers: [{
      clientId: '1234',
      nonce: '234234',
      configURL: 'https://idp.example/enterprise/fedcm.json',
    },
  }
});

因此,只有 '4567' 的账号 ID 可供该用户登录 位置浏览器会静默隐藏 '123' 的账号 ID, 不会与该网站上的 IdP 不支持的账号一起提供。

源试用:FedCM 作为 Storage Access API 的信任信号

Chrome 126 将开始 FedCM 源试用,作为 存储空间访问权限 API。包含 此次变更,通过 FedCM 提前授予的权限将成为 由 Storage Access 自动批准存储空间访问请求 API

当嵌入式 iframe 想要访问个性化资源时,这非常有用: 例如,如果 idp.example 嵌入到 rp.example 中,并且需要显示 个性化资源如果浏览器限制访问第三方 Cookie 即使用户通过 FedCM 使用 idp.example 登录 rp.example, 嵌入式 idp.example iframe 将无法请求个性化资源 因为请求不会包含第三方 Cookie

为此,idp.example 需要通过其 网站上嵌入的 iframe,并且只能通过 权限提示。

使用 FedCM 作为存储访问的信任信号 API, Storage Access API 权限检查不仅接受授予 存储访问提示时授予了 权限,同时由管理员授予的权限 FedCM 提示。

// In top-level rp.example:

// Ensure FedCM permission has been granted.
const cred = await navigator.credentials.get({
  identity: {
    providers: [{
      configURL: 'https://idp.example/fedcm.json',
      clientId: '123',
    }],
  },
  mediation: 'optional',
});

// In an embedded IdP iframe:

// No user gesture is needed to call this, and the call will be auto-granted.
await document.requestStorageAccess();

// This returns `true`.
const hasAccess = await document.hasStorageAccess();

用户使用 FedCM 登录后,只要 FedCM 身份验证有效。这意味着,当用户断开连接后, 系统会显示提示

参与源试用

您可以在本地试用 FedCM Continuation API 包,只需启用 标志 chrome://flags#fedcm-authz(Chrome 126 或更高版本)。您还可以尝试 FedCM 作为本地 Storage Access API 的信任信号 #fedcm-with-storage-access-api(Chrome 126 或更高版本)。

这些功能也以源试用的形式提供。通过源试用,您可以试用新功能,并就其易用性、实用性和有效性提供反馈。如需了解详情,请参阅开始试用

试用 FedCM Continuation API 包源 试用、 创建两个源试用令牌:

如果您有兴趣启用 Continuation API 以及按钮 流程,请启用按钮模式 API 来源 试用 以及:

尝试将 FedCM 作为 Storage Access API 源的信任信号 试用

Continuation API 软件包源试用以及 FedCM 作为 Chrome 126 及更高版本现已提供 Storage Access API 源试用。

为 RP 注册第三方源试用

  1. 转到源试用注册页面。
  2. 点击 Register(注册)按钮,然后填写申请令牌的表单。
  3. Web Origin 字段中输入 IdP 的来源。
  4. 检查第三方匹配,以便在其他源上使用 JavaScript 注入令牌。
  5. 点击提交
  6. 将颁发的令牌嵌入第三方网站。

若要在第三方网站上嵌入令牌,请将以下代码添加到 IdP 的 从 IdP 的来源提供的 JavaScript 库或 SDK。

const tokenElement = document.createElement('meta');
tokenElement.httpEquiv = 'origin-trial';
tokenElement.content = 'TOKEN_GOES_HERE';
document.head.appendChild(tokenElement);

TOKEN_GOES_HERE 替换为您自己的令牌。