在身份提供方端使用 FedCM 实现身份解决方案

FedCM 实现包括针对身份提供方 (IdP)依赖方 (RP) 的多个核心步骤。如需了解如何RP 端实现 FedCM,请参阅相关文档。

IdPs 必须完成以下步骤才能实现 FedCM:

  1. 创建知名文件
  2. 创建配置文件
  3. 创建以下端点:
  4. 向浏览器告知用户登录状态

创建 well-known 文件

为防止跟踪器滥用该 API,必须从 IdP 的 eTLD+1/.well-known/web-identity 提供一个 well-known 文件。

知名文件可以包含以下属性:

属性 必需 说明
provider_urls 必需 IdP 配置文件路径的数组。如果指定了 accounts_endpointlogin_url,则忽略此属性(但仍需指定)。
accounts_endpoint 推荐,需要 login_url
账号端点的网址。这样一来,只要每个配置文件使用相同的 login_urlaccounts_endpoint 网址,就可以支持多种配置。

注意:此参数从 Chrome 132 开始受支持。
login_url 推荐,需要 accounts_endpoint 用户登录 IdP 的登录页面网址。这样一来,只要每个配置文件使用相同的 login_urlaccounts_endpoint,就可以支持多种配置。

注意:此参数从 Chrome 132 及更高版本开始受支持。

例如,如果 IdP 端点在 https://accounts.idp.example/ 下提供,则它们必须在 https://idp.example/.well-known/web-identity 下提供一个 .well-known 文件以及一个 IdP 配置文件。以下是 well-known 文件内容的示例:

 {  "provider_urls": ["https://accounts.idp.example/config.json"]  } 

IdP 可以通过在 well-known 文件中指定 accounts_endpointlogin_url 来容纳 IdP 的多个配置文件。此功能在以下情况下非常有用:

  • IdP 需要支持多种不同的测试和生产配置。
  • IdP 需要支持每个区域的不同配置(例如 eu-idp.exampleus-idp.example)。

为了支持多种配置(例如,区分测试环境和生产环境),IdP 必须指定 accounts_endpointlogin_url

 {  // This property is required, but will be ignored when IdP supports  // multiple configs (when `accounts_endpoint` and `login_url` are  // specified), as long as `accounts_endpoint` and `login_url` in  // that config file match those in the well-known file.  "provider_urls": [ "https://idp.example/fedcm.json" ],  // Specify accounts_endpoint and login_url properties to support  // multiple config files.  // Note: The accounts_endpoint and login_url must be identical  // across all config files. Otherwise,  // the configurations won't be supported.  "accounts_endpoint": "https://idp.example/accounts",  "login_url": "https://idp.example/login"  } 

创建 IdP 配置文件和端点

IdP 配置文件为浏览器提供了一份必需端点列表。IdP 必须托管一个或多个配置文件,以及必需的端点和网址。所有 JSON 响应都必须以 application/json 内容类型传送。

配置文件网址由在 RP 上执行的 navigator.credentials.get() 调用提供的值确定。RP 将传递每个身份提供商的配置文件网址:

 // Executed on RP's side:  try {  const credential = await navigator.credentials.get({  identity: {  providers: [  {  // To allow users to sign in with the IdP1 using FedCM, RP specifies the IdP's config file URL:  configUrl: 'https://idp1.example/foo.json', // first IdP  clientId: '123',  },  // To allow users to sign in with the IdP2 using FedCM, RP specifies the IdP's config file URL.  // Note that multiple IdPs in a single get() are supported from Chrome 136.  {  configUrl: 'https://idp2.example/bar.json', // second IdP  clientId: '456',  },  ],  },  });  const token = credential.token;  // 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  }  } catch (error) {  // handle error  } 

浏览器将通过 GET 请求来提取配置文件,但该请求不包含 Origin 标头或 Referer 标头。请求不含 Cookie,也不遵循重定向。这样可有效防止 IdP 了解是谁发出了请求以及哪个 RP 正在尝试连接。例如:

 GET /config.json HTTP/1.1  Host: accounts.idp.example  Accept: application/json  Sec-Fetch-Dest: webidentity 

IdP 必须实现一个配置端点,该端点会返回 JSON。JSON 包含以下属性:

属性 说明
accounts_endpoint(必需) 账号端点的网址。
account_label(可选) 自定义账号标签字符串,用于确定在使用此配置文件时应返回哪些账号,例如:"account_label": "developer"
IdP 可以按如下方式实现自定义账号标签:
  • 创建与特定标签相关联的配置文件(使用此 account_label 参数)。
  • 在账号端点中指定 标签

例如,IdP 实现 "https://idp.example/developer-config.json" 配置文件,并指定 "account_label": "developer"。IdP 还会使用账号端点中的 label_hints 参数,将某些账号标记为 "developer" 标签。当 RP 调用 navigator.credentials.get() 并指定 "https://idp.example/developer-config.json" 配置文件时,系统只会返回具有 "developer" 标签的账号。

注意:Chrome 132 及更高版本支持自定义账号标签
supports_use_other_account(可选) 一个布尔值,用于指定用户是否可以使用与当前登录账号不同的账号登录(如果 IdP 支持多个账号)。此设置仅适用于活动模式。
client_metadata_endpoint(可选) 客户端元数据端点的网址。
id_assertion_endpoint(必需) 身份断言端点的网址。
disconnect(可选) 断开连接端点的网址。
login_url(必需) 供用户登录 IdP 的登录页面网址
branding(可选) 包含各种品牌推广选项的对象。
branding.background_color(可选) 用于设置“以…身份继续”按钮的背景颜色的品牌推广选项。使用相关的 CSS 语法,即 hex-colorhsl()rgb()named-color
branding.color(可选) 用于设置“以...身份继续”按钮的文字颜色的品牌推广选项。使用相关的 CSS 语法,即 hex-colorhsl()rgb()named-color
branding.icons(可选) 图标对象数组。这些图标会显示在登录对话框中。图标对象有两个参数:
  • url(必需):图标图片的网址。此功能不支持 SVG 图片。
  • size(可选):应用假定的图标尺寸,为正方形且分辨率单一。在被动模式下,此数值必须大于或等于 25 像素;在主动模式下,此数值必须大于或等于 40 像素。

以下是 IdP 的响应正文示例:

 {  "accounts_endpoint": "/accounts.example",  "client_metadata_endpoint": "/client_metadata.example",  "id_assertion_endpoint": "/assertion.example",  "disconnect_endpoint": "/disconnect.example",  "login_url": "/login",  // When RPs use this config file, only those accounts will be  //returned that include `developer` label in the accounts endpoint.  "account_label": "developer",  "supports_use_other_account": true,  "branding": {  "background_color": "green",  "color": "#FFEEAA",  "icons": [{  "url": "https://idp.example/icon.ico",  "size": 25  }]  }  } 

浏览器提取配置文件后,会向 IdP 端点发送后续请求:

IdP 端点
IdP 端点

使用其他账号

如果 IdP 支持多个账号或替换现有账号,用户可以切换到与当前登录账号不同的账号。

如需允许用户选择其他账号,IdP 必须在配置文件中指定此功能:

 {  "accounts_endpoint" : "/accounts.example",  "supports_use_other_account": true  } 

账号端点

IdP 的账号端点会返回用户在 IdP 上登录的账号列表。如果 IdP 支持多个账号,此端点将返回所有已登录的账号。

浏览器发送包含 Cookie 的 GET 请求,但该请求包含 SameSite=None,不包含 client_id 参数、Origin 标头或 Referer 标头。这样可以有效地防止 IdP 了解用户尝试登录哪个 RP。例如:

 GET /accounts.example HTTP/1.1  Host: accounts.idp.example  Accept: application/json  Cookie: 0x23223  Sec-Fetch-Dest: webidentity 

收到请求后,服务器应执行以下操作:

  1. 验证请求是否包含 Sec-Fetch-Dest: webidentity HTTP 标头。
  2. 将会话 Cookie 与已登录账号的 ID 进行匹配。
  3. 返回账号列表。

浏览器需要一个 JSON 响应,其中包含一个 accounts 属性,该属性包含一个具有以下属性的账号信息数组:

属性 说明
id(必需) 用户的唯一 ID。
name 根据用户的语言区域和偏好设置显示的用户全名。

注意:从 Chrome 141 开始,必须提供 nameemailusernametel 参数中的至少一个。在早期 Chrome 版本中,nameemail 都是必需的。
username 用户选择的用户名。

注意:从 Chrome 141 开始,必须提供 nameemailusernametel 参数中的至少一个。
email 用户的电子邮件地址。

注意:从 Chrome 141 开始,必须提供 nameemailusernametel 参数中的至少一个。在早期 Chrome 版本中,nameemail 都是必需的。
tel 用户的电话号码。

注意:从 Chrome 141 开始,必须提供 nameemailusernametel 参数中的至少一个。
picture(可选) 用户头像图片的网址。
given_name(可选) 用户的名字。
approved_clients(可选) 用户已注册的 RP 客户端 ID 数组。
login_hints(可选) IdP 支持的所有可能的过滤器类型组成的数组,用于指定账号。RP 可以使用 loginHint 属性调用 navigator.credentials.get(),以选择性地显示指定账号。
domain_hints(可选) 相应账号关联的所有网域的数组。RP 可以使用 domainHint 属性调用 navigator.credentials.get() 来过滤账号。
label_hints(可选) 账号关联的自定义账号标签的字符串数组。
IdP 可以按如下方式实现自定义账号标签:
  • 在账号端点中指定账号标签(使用 label_hints 参数)。
  • 为每个特定标签创建配置文件

例如,IdP 实现 https://idp.example/developer-config.json 配置文件,并指定 "account_label": "developer"。IdP 还会使用 账号端点中的 label_hints 参数,为某些账号添加 "developer" 标签。当 RP 调用 navigator.credentials.get() 并指定 https://idp.example/developer-config.json 配置文件时,系统只会返回具有 "developer" 标签的账号。

自定义账号标签与登录提示和网域提示的不同之处在于,前者完全由 IdP 服务器维护,RP 仅指定要使用的配置文件。

注意:Chrome 132 及更高版本支持自定义账号标签

响应正文示例:

 {  "accounts": [{  "id": "1234",  "given_name": "John",  "name": "John Doe",  "email": "john_doe@idp.example",  "picture": "https://idp.example/profile/123",  // Ids of those RPs where this account can be used  "approved_clients": ["123", "456", "789"],  // This account has 'login_hints`. When an RP calls `navigator.credentials.get()`  // with a `loginHint` value specified, for example, `exampleHint`, only those  // accounts will be shown to the user whose 'login_hints' array contains the `exampleHint`.  "login_hints": ["demo1", "exampleHint"],  // This account is labelled. IdP can implement a specific config file for a  // label, for example, `https://idp.example/developer-config.json`. Like that  // RPs can filter out accounts by calling `navigator.credentials.get()` with  // `https://idp.example/developer-config.json` config file.  "label_hints": ["enterprise", "developer"]  }, {  "id": "5678",  "given_name": "Johnny",  "name": "Johnny",  "email": "johnny@idp.example",  "picture": "https://idp.example/profile/456",  "approved_clients": ["abc", "def", "ghi"],  "login_hints": ["demo2"],  "domain_hints": ["@domain.example"]  }]  } 

如果用户未登录,请回复 HTTP 401(未获授权)。

返回的账号列表由浏览器使用,不会提供给 RP。

ID 断言端点

IdP 的 ID 断言端点会为其已登录的用户返回断言。当用户使用 navigator.credentials.get() 调用登录 RP 网站时,浏览器会向此端点发送一个 POST 请求,其中包含 SameSite=None 的 Cookie 和 application/x-www-form-urlencoded 的内容类型,以及以下信息:

属性 说明
client_id(必需) RP 的客户端标识符。
account_id(必需) 登录用户的唯一 ID。
disclosure_text_shown 结果为 "true""false" 字符串(而非布尔值)。在以下情况下,结果为 "false"
  • 如果因 RP 的客户端 ID 包含在来自 accounts 端点的响应的 approved_clients 属性列表中而未显示披露声明文本。
  • 如果由于浏览器在没有 approved_clients 的情况下在过去观察到注册时刻而未显示披露声明文本。
  • 如果 fields 参数未包含所有三个字段(“name”“email”和“picture”),例如 fields=[ ]fields=['name', 'picture']。这是为了与旧版实现向后兼容。

    注意:从 Chrome 141 开始,即使 disclosure_text_shown 值为 "false",也可能会显示披露声明文本。如需验证披露声明文本是否已显示,请改为检查 disclosure_shown_for 值。
disclosure_shown_for 列出浏览器在披露声明文本中向用户显示的字段,以通知用户 RP 正在向 IdP 请求哪些数据。
is_auto_selected 如果对 RP 执行了自动重新身份验证,则 is_auto_selected 表示 "true"。否则为 "false"。这有助于支持更多与安全相关的功能。例如,某些用户可能偏好安全性更高的层级,该层级要求在身份验证过程中进行明确的用户中介。如果 IdP 收到未经此类中介的令牌请求,则可能会以不同的方式处理该请求。例如,返回一个错误代码,以便 RP 可以使用 mediation: required 再次调用 FedCM API。
fields(可选) 一个字符串数组,用于指定 RP 请求 IdP 共享的用户信息。您可以选择指定以下字段:
  • "name"
  • "username"
  • "email"
  • "tel"
  • "picture"
浏览器将发送 fieldsdisclosure_text_showndisclosure_shown_for,并在 POST 请求中列出指定的字段,如以下示例所示。

注意:Chrome 132 及更高版本支持 Fields API。Chrome 141 及更高版本支持“username”和“tel”字段。
params(可选) 任何有效的 JSON 对象,用于指定其他自定义键值参数,例如:
  • scope:包含 RP 需要请求的其他权限的字符串值,例如 "drive.readonly calendar.readonly"
  • nonce:RP 提供的随机字符串,用于确保响应是针对此特定请求而发出的。防止重放攻击。
  • 其他自定义键值对参数。
当浏览器发送 POST 请求时,params 值将序列化为 JSON,然后进行百分比编码

注意:Chrome 132 及更高版本支持 Parameters API。

HTTP 标头示例:

 POST /assertion.example HTTP/1.1  Host: accounts.idp.example  Origin: https://rp.example/  Content-Type: application/x-www-form-urlencoded  Cookie: 0x23223  Sec-Fetch-Dest: webidentity  // disclosure_text_shown is set to 'false', as the 'name' field value is missing in 'fields' array  // params value is serialized to JSON and then percent-encoded.  account_id=123&client_id=client1234&disclosure_text_shown=false&is_auto_selected=true&params=%22%7B%5C%22nonce%5C%22%3A%5C%22nonce-value%5C%22%7D%22.%0D%0A4&fields=email,picture&disclosure_shown_for=email,picture 

收到请求后,服务器应执行以下操作:

  1. 使用 CORS(跨域资源共享)响应请求。
  2. 验证请求是否包含 Sec-Fetch-Dest: webidentity HTTP 标头。
  3. Origin 标头与由 client_id 确定的 RP 源进行匹配。如果不匹配,则拒绝。
  4. account_id 与已登录账号的 ID 进行匹配。如果不匹配,则拒绝。
  5. 使用令牌进行响应。如果请求被拒绝,请以错误响应进行响应。

IdP 可以自行决定如何发放令牌。一般来说,它会使用账号 ID、客户端 ID、签发者来源和随机数等信息进行签名,以便 RP 验证令牌是否真实。

浏览器需要包含以下属性的 JSON 响应:

属性 说明
token 令牌是一个字符串,其中包含有关身份验证的声明。
continue_on 可实现多步登录流程的重定向网址。

浏览器会将返回的令牌传递给 RP,以便 RP 验证身份验证。

 {  // IdP can respond with a token to authenticate the user  "token": "***********"  } 

继续使用功能

IdP 可以在 ID 断言端点响应中提供重定向网址,以实现多步登录流程。当 IdP 需要请求其他信息或权限时,此功能非常有用,例如:

  • 用于访问用户服务器端资源的权限。
  • 验证联系信息是否为最新状态。
  • 家长控制。

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 dialog window:  "continue_on": "https://idp.example/continue_on_url"  } 

如果响应包含 continue_on 参数,系统会打开一个新的对话框窗口,并将用户导航到指定路径。在用户与 continue_on 页面互动后,IdP 应调用 IdentityProvider.resolve() 并将令牌作为实参传递,以便解析原始 navigator.credentials.get() 调用的 promise:

 document.getElementById('example-button').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.resolve() 调用是父窗口 (RP) 和对话框窗口 (IdP) 进行通信的唯一方式。
如果用户拒绝请求,IdP 可以通过调用 IdentityProvider.close() 关闭窗口。

 IdentityProvider.close(); 

Continuation API 需要用户明确互动(点击)才能正常运行。 下面介绍了 Continuation API 如何与不同的中介模式搭配使用:

  • passive 模式下:
    • mediation: 'optional'(默认):延续 API 仅在用户手势(例如点击网页上的按钮或 FedCM 界面上的按钮)后才会生效。当在没有用户手势的情况下触发自动重新身份验证时,系统不会打开对话框窗口,并且会拒绝 Promise。
    • mediation: 'required':始终要求用户互动,因此 Continuation API 始终有效。
  • 活跃模式下:
    • 始终需要用户激活。延续 API 始终兼容。

如果用户出于某种原因在对话框中更改了其账号(例如,IdP 提供了“使用其他账号”功能,或者在委托情况下),则解析调用会采用可选的第二个实参,从而允许类似以下操作:

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

返回错误响应

id_assertion_endpoint 还可以返回“错误”响应,其中包含两个可选字段:

  • code:IdP 可以从 OAuth 2.0 指定的错误列表invalid_requestunauthorized_clientaccess_deniedserver_errortemporarily_unavailable)中选择一个已知错误,也可以使用任意字符串。如果是后者,Chrome 会呈现包含一般性错误消息的错误界面,并将代码传递给 RP。
  • url:用于标识包含错误相关信息的人类可读网页,以便向用户提供有关错误的更多信息。此字段对用户很有用,因为浏览器无法在内置界面中提供丰富的错误消息。例如:后续步骤的链接或客户服务联系信息。如果用户想详细了解错误详情以及如何修复错误,可以访问浏览器界面中提供的网页,获取更多详细信息。该网址必须与 IdP configURL 位于同一网站。
 // id_assertion_endpoint response  {  "error" : {  "code": "access_denied",  "url" : "https://idp.example/error?type=access_denied"  }  } 

自定义账号标签

借助自定义账号标签,IdP 可以使用标签注释用户账号,RP 可以通过为特定标签指定 configURL,选择仅提取具有特定标签的账号。当 RP 需要按特定条件过滤账号时,这会非常有用,例如,仅显示特定角色的账号,如 "developer""hr"

通过在 navigator.credentials.get() 调用中指定网域提示登录提示功能,可以实现类似的过滤。不过,自定义账号标签可以通过指定配置文件来过滤用户,这在使用多个 config网址 时尤其有用。自定义账号标签也不同,因为它们是从 IdP 服务器提供的,而不是像登录或网域提示那样从 RP 提供的。

假设某个 IdP 想要区分 "developer" 账号和 "hr" 账号。为此,IdP 需要分别为 "developer""hr" 支持两个 config网址:

  • 开发者配置文件 https://idp.example/developer/fedcm.json 具有 "developer" 标签,企业配置文件 https://idp.example/hr/fedcm.json 具有 "hr" 标签,如下所示:
 // The developer config file at `https://idp.example/developer/fedcm.json`  {  "accounts_endpoint": "https://idp.example/accounts",  "client_metadata_endpoint": "/client_metadata",  "login_url": "https://idp.example/login",  "id_assertion_endpoint": "/assertion",  "account_label": "developer"  } 
 // The hr config file at `https://idp.example/hr/fedcm.json`  {  "accounts_endpoint": "https://idp.example/accounts",  "client_metadata_endpoint": "/client_metadata",  "login_url": "https://idp.example/login",  "id_assertion_endpoint": "/assertion",  "account_label": "hr"  } 
  • 在这种设置下,well-known 文件应包含 accounts_endpointlogin_url,以允许使用多个 config网址:
 {  "provider_urls": [ "https://idp.example/fedcm.json" ],  "accounts_endpoint": "https://idp.example/accounts",  "login_url": "https://idp.example/login"  } 
  • 通用 IdP 账号端点(在本例中为 https://idp.example/accounts)会返回一个账号列表,其中包含一个 label_hints 属性,该属性针对每个账号以数组形式包含分配的标签:
 {  "accounts": [{  "id": "123",  "given_name": "John",  "name": "John Doe",  "email": "john_doe@idp.example",  "picture": "https://idp.example/profile/123",  "label_hints": ["developer"]  }], [{  "id": "4567",  "given_name": "Jane",  "name": "Jane Doe",  "email": "jane_doe@idp.example",  "picture": "https://idp.example/profile/4567",  "label_hints": ["hr"]  }]  } 

如果 RP 希望允许 "hr" 用户登录,可以在 navigator.credentials.get() 调用中指定 config网址 https://idp.example/hr/fedcm.json

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

因此,用户只能使用 4567 的账号 ID 登录。 浏览器会以静默方式隐藏 123 的账号 ID,这样用户就不会获得此网站上的 IdP 不支持的账号。

其他注意事项:

  • 标签是字符串。如果 label_hints 数组或 account_label 字段使用的值不是字符串,则该值会被忽略。
  • 如果未在 configURL 中指定任何标签,则所有账号都将显示在 FedCM 账号选择器中。
  • 如果未为账号指定任何标签,则仅当 configURL 也未指定标签时,该账号才会显示在账号选择器中。
  • 如果被动模式下没有账号与所请求的标签匹配(类似于“网域提示”功能),FedCM 对话框会显示登录提示,允许用户登录 IdP 账号。对于主动模式,系统会直接打开登录对话框窗口。

断开端点连接

通过调用 IdentityCredential.disconnect(),浏览器会向此断开连接端点发送一个包含 Cookie 的跨源 POST 请求,该请求具有 SameSite=Noneapplication/x-www-form-urlencoded 的内容类型,并包含以下信息:

属性 说明
account_hint IdP 账号的提示。
client_id RP 的客户端标识符。
 POST /disconnect.example HTTP/1.1  Host: idp.example  Origin: rp.example  Content-Type: application/x-www-form-urlencoded  Cookie: 0x123  Sec-Fetch-Dest: webidentity  account_hint=account456&client_id=rp123 

收到请求后,服务器应执行以下操作:

  1. 使用 CORS(跨域资源共享)响应请求。
  2. 验证请求是否包含 Sec-Fetch-Dest: webidentity HTTP 标头。
  3. Origin 标头与由 client_id 确定的 RP 源进行匹配。如果不匹配,则拒绝。
  4. account_hint 与已登录账号的 ID 进行匹配。
  5. 断开用户账号与 RP 的连接。
  6. 以 JSON 格式向浏览器返回已识别的用户账号信息。

JSON 响应载荷示例如下所示:

 {  "account_id": "account456"  } 

相反,如果 IdP 希望浏览器断开与 RP 关联的所有账号,请传递与任何账号 ID 都不匹配的字符串,例如 "*"

客户端元数据端点

IdP 的客户端元数据端点会返回信赖方的元数据,例如 RP 的隐私权政策、服务条款和徽标图标。RP 应提前向 IdP 提供指向其隐私权政策和服务条款的链接。如果用户尚未在 RP 上向 IdP 注册,登录对话框中会显示这些链接。

浏览器使用 client_id navigator.credentials.get 发送 GET 请求,但不包含 Cookie。如果从跨网站来源调用 FedCM API,系统还会发送 top_frame_origin 参数。例如:

 GET /client_metadata.example?client_id=1234&top_frame_origin=https%3A%2F%2Ftop-frame.example HTTP/1.1  Host: accounts.idp.example  Origin: https://rp.example/  Accept: application/json  Sec-Fetch-Dest: webidentity 

收到请求后,服务器应执行以下操作:

  1. 确定 client_id 的 RP。
  2. [可选] 如果请求中包含 top_frame_origin,请检查 RP 和顶级框架是否属于同一方,以确定浏览器是否应调用 iframe 网域。IdP 应定义其自定义逻辑来确定这一点。
  3. 使用客户端元数据进行响应。

客户端元数据端点的属性包括:

属性 说明
privacy_policy_url(可选) RP 隐私权政策网址。
terms_of_service_url(可选) RP 服务条款网址。
icons(可选) 对象数组,例如 [{ "url": "https://rp.example/rp-icon.ico", "size": 40}]
client_is_third_party_to_top_frame_origin(可选) 一个布尔值,用于指示 RP 是否嵌入在顶级网站的第三方 iframe 中。如果设置为 true,FedCM 界面将显示调用 API 的 iframe 的网域,以及顶级网站的网域。

浏览器会要求从端点返回 JSON 响应:

 {  "privacy_policy_url": "https://rp.example/privacy_policy.html",  "terms_of_service_url": "https://rp.example/terms_of_service.html",  "icons": [{  "url": "https://rp.example/rp-icon.ico",  "size": 40  }]  } 

返回的客户端元数据由浏览器使用,不会提供给 RP。

登录网址

此端点用于让用户登录 IdP。

借助 Login Status API,IdP 必须将用户的登录状态告知浏览器。不过,状态可能会不同步,例如当会话过期时。在这种情况下,浏览器可以通过 idp 配置文件login_url 中指定的登录页面网址,动态允许用户登录 IdP。

FedCM 对话框会显示一条建议登录的消息,如下图所示。

建议登录 IdP 的 FedCM 对话框。
建议登录 IdP 的 FedCM 对话框。

当用户点击继续按钮时,浏览器会打开一个对话框窗口,其中包含 IdP 的登录页面。

FedCM 对话框示例。
点击“登录到 IdP”按钮后显示的示例对话框。

该对话框是一个包含第一方 Cookie 的常规浏览器窗口。对话框中发生的一切都由 IdP 决定,并且没有可用于向 RP 页面发出跨源通信请求的窗口句柄。用户登录后,IdP 应执行以下操作:

  • 发送 Set-Login: logged-in 标头或调用 navigator.login.setStatus("logged-in") API,以告知浏览器用户已登录。
  • 调用 IdentityProvider.close() 以关闭对话框。
用户使用 FedCM 登录 IdP 后,再登录 RP。

告知浏览器用户的登录状态

登录状态 API 是一种机制,网站(尤其是 IdP)通过该机制告知浏览器用户在 IdP 上的登录状态。借助此 API,浏览器可以减少向 IdP 发送的不必要请求,并缓解潜在的时序攻击

当用户登录 IdP 或从所有 IdP 账号中退出登录时,IdP 可以通过发送 HTTP 标头或调用 JavaScript API 向浏览器发送用户登录状态信号。对于每个 IdP(通过其配置网址进行标识),浏览器都会保留一个三态变量,用于表示登录状态,可能的值包括:

  • logged-in
  • logged-out
  • unknown(默认)
登录状态 说明
logged-in 当用户的登录状态设置为 logged-in 时,调用 FedCM 的 RP 会向 IdP 的账号端点发送请求,并在 FedCM 对话框中向用户显示可用的账号。
logged-out 当用户的登录状态为 logged-out 时,调用 FedCM 会静默失败,而不会向 IdP 的账号端点发出请求。
unknown(默认) 在 IdP 使用登录状态 API 发送信号之前,系统会设置 unknown 状态。当状态为 unknown 时,浏览器会向 IdP 的账号端点发出请求,并根据账号端点的响应更新状态。

如需表明用户已登录,请在 IdP 源的顶级导航或同站子资源请求中发送 Set-Login: logged-in HTTP 标头:

 Set-Login: logged-in 

或者,在顶级导航中从 IdP 来源调用 JavaScript 方法 navigator.login.setStatus('logged-in')

 navigator.login.setStatus('logged-in') 

用户的登录状态将设置为 logged-in

如需表明用户已从其所有账号中退出,请在顶级导航或 IdP 源的同站子资源请求中发送 Set-Login: logged-out HTTP 标头:

 Set-Login: logged-out 

或者,在顶级导航中从 IdP 来源调用 JavaScript API navigator.login.setStatus('logged-out')

 navigator.login.setStatus('logged-out') 

用户的登录状态将设置为 logged-out

unknown 状态是在 IdP 使用登录状态 API 发送信号之前设置的。浏览器向 IdP 的账号端点发出请求,并根据账号端点的响应更新状态:

  • 如果端点返回活跃账号列表,请将状态更新为 logged-in 并打开 FedCM 对话框以显示这些账号。
  • 如果端点未返回任何账号,请将状态更新为 logged-out 并使 FedCM 调用失败。

允许用户通过动态登录流程登录

即使 IdP 会持续向浏览器告知用户的登录状态,但该状态也可能会不同步,例如在会话过期时。当登录状态为 logged-in 时,浏览器会尝试向账号端点发送包含凭据的请求,但由于会话不再可用,服务器不会返回任何账号。在这种情况下,浏览器可以通过对话框动态允许用户登录 IdP