Chrome 120 为 FedCM 提供了 Login Status API。Login Status API(以前称为 IdP Sign-in Status API)允许网站(尤其是身份提供方)在用户登录和退出时向浏览器发出信号。FedCM 使用此信号来解决静默计时攻击问题,并因此让 FedCM 无需使用第三方 Cookie 即可运行。此更新解决了我们之前在 FedCM 原始意向 (FedCM) 中发现的最后一项向后不兼容的更改,属于我们的工作范围。
虽然 Login Status API 改进了 Privacy 属性和易用性,但这项变更一旦推出便不向后兼容。如果您已有 FedCM 实现,请务必按照以下说明进行更新。
此外,Chrome 还提供两项新的联合凭据管理 (FedCM) 功能:
- Error API:当用户的登录尝试失败时,根据来自 ID 断言端点的服务器响应(如果有)通过原生界面通知用户。
- Auto-Selected Flag API:如果流程中自动选择了凭据,则通知身份提供方 (IdP) 和依赖方 (RP)。
Login Status API
Login Status API 是一种机制,可供网站(尤其是 IdP)告知浏览器用户在 IdP 上的登录状态。借助此 API,浏览器可以减少向 IdP 发送的不必要请求并缓解潜在的计时攻击。
将用户的登录状态告知浏览器
当用户在 IdP 上登录或退出其所有 IdP 帐号时,IdP 可以通过以下两种方式向浏览器告知用户的登录状态:发送 HTTP 标头或调用 JavaScript API。对于每个 IdP(由其配置网址标识),浏览器都会保留一个表示登录状态的三态变量,可能的值包括 logged-in
、logged-out
和 unknown
。默认状态为 unknown
。
如需表明用户已登录,请在顶级导航或同源子资源请求中发送 Set-Login: logged-in
HTTP 标头:
Set-Login: logged-in
或者,从 IdP 来源调用 JavaScript API navigator.login.setStatus('logged-in')
:
navigator.login.setStatus('logged-in');
此类调用会将用户的登录状态记录为 logged-in
。当用户的登录状态设置为 logged-in
时,调用 FedCM 的 RP 会向 IdP 的帐号列表端点发出请求,并在 FedCM 对话框中向用户显示可用帐号。
如需表明用户已退出其所有帐号,请在顶级导航或同源子资源请求中发送 Set-Login:
logged-out
HTTP 标头:
Set-Login: logged-out
或者,从 IdP 来源调用 JavaScript API navigator.login.setStatus('logged-out')
:
navigator.login.setStatus('logged-out');
此类调用会将用户的登录状态记录为 logged-out
。当用户登录状态为 logged-out
时,调用 FedCM 会静默失败,而不会向 IdP 的帐号列表端点发出请求。
unknown
状态在 IdP 发送信号之前使用 Login Status API 设置。我们引入此状态是为了更好地进行转换,因为用户可能已经在我们推出此 API 时登录了 IdP。在首次调用 FedCM 时,IdP 可能没有机会向浏览器发出信号。在这种情况下,我们会向 IdP 的帐号列表端点发出请求,并根据来自帐号列表端点的响应更新状态:
- 如果端点返回活跃帐号的列表,请将状态更新为
logged-in
,并打开 FedCM 对话框以显示这些帐号。 - 如果端点未返回任何帐号,请将状态更新为
logged-out
并使 FedCM 调用失败。
如果用户会话过期,该怎么办?让用户通过动态登录流程登录!
即使 IdP 不断通知用户的浏览器登录状态,状态也可能不同步,例如当会话过期时。当登录状态为 logged-in
时,浏览器会尝试向帐号列表端点发送存储了凭据的请求,但由于会话不再可用,服务器不会返回任何帐号。在这种情况下,浏览器可以通过对话框窗口动态地让用户登录 IdP。
FedCM 对话框会显示建议登录的消息,如下图所示。
当用户点击继续按钮时,浏览器会打开 IdP 登录页面的对话框。
登录页面网址在 IdP 配置文件中使用 login_url
指定。
{
"accounts_endpoint": "/auth/accounts",
"client_metadata_endpoint": "/auth/metadata",
"id_assertion_endpoint": "/auth/idtokens",
"login_url": "/login"
}
}
该对话框是包含第一方 Cookie 的常规浏览器窗口。对话框中发生的情况由 IdP 决定,没有可用于向 RP 页面发出跨源通信请求的窗口句柄。用户登录后,IdP 应执行以下操作:
- 发送
Set-Login: logged-in
标头或调用navigator.login.setStatus("logged-in")
API,以通知浏览器用户已登录。 - 调用
IdentityProvider.close()
以关闭对话框。
您可以在我们的演示中试用 Login Status API 行为。
- 点按转到 IdP 并登录按钮。
- 使用任意帐号登录。
- 从帐号状态下拉列表中选择会话已过期。
- 按更新个人信息按钮。
- 点按 Visit the RP to try FedCM(访问 RP 以尝试 FedCM)按钮。
您应该能够通过模块行为观察登录 IdP 的情况。
错误 API
当 Chrome 向 ID 断言端点发送请求时(例如,当用户点击 FedCM 界面上的 Continue as 按钮时,或触发自动重新身份验证时),IdP 可能会因正当原因而无法颁发令牌。 例如,如果客户端未经授权,则服务器暂时不可用,等等。目前,如果发生此类错误,Chrome 会以静默方式使请求失败,并且仅通过拒绝 promise 来通知 RP。
借助 Error API,Chrome 会使用 IdP 提供的错误信息显示原生界面,从而通知用户。
IdP HTTP API
在 id_assertion_endpoint
响应中,如果可以根据请求颁发令牌,则 IdP 可以将令牌返回给浏览器。在此方案中,如果无法颁发令牌,IdP 可以返回“错误”响应,其中包含两个新的可选字段:
code
url
// id_assertion_endpoint response
{
"error": {
"code": "access_denied",
"url": "https://idp.example/error?type=access_denied"
}
}
对于代码,IdP 可以从 OAuth 2.0 指定错误列表 [invalid_request
、unauthorized_client
、access_denied
、server_error
和 temporarily_unavailable
] 中选择一个已知错误,也可以使用任意字符串。如果是后者,Chrome 会使用一般错误消息呈现错误界面,并将代码传递给 RP。
对于 url
,它会标识包含错误相关信息的人类可读网页,以便向用户提供有关错误的更多信息。此字段对用户很有用,因为浏览器无法在原生界面中提供丰富的错误消息。例如,后续步骤链接、客户服务联系信息等。如果用户想要详细了解错误详情及解决方法,可以从浏览器界面访问所提供的页面,获取更多详情。该网址必须与 IdP configURL
位于同一网站。
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;
}
自动选择标记 API
mediation: optional
是 Credential Management API 中的默认用户中介行为,它会尽可能触发自动重新进行身份验证。但是,由于只有浏览器知道原因,自动重新身份验证可能不可用;当自动重新身份验证不可用时,系统可能会提示用户通过显式用户中介进行登录,该流程具有不同的属性。
- 从 API 调用方的角度来看,当他们收到 ID 令牌时,无法得知这是否是由自动重新身份验证流程引起的。这使得他们很难评估 API 性能并相应地改善用户体验。
- 从 IdP 的角度来看,他们同样无法判断是否进行了自动重新身份验证,以便评估性能。此外,是否涉及明确的用户中介可以帮助他们支持更多与安全相关的功能。例如,某些用户可能更喜欢更高的安全层级,这要求身份验证中需要明确的用户中介。如果 IdP 在没有使用此类中介的情况下收到令牌请求,他们可能会以不同的方式处理该请求。例如,返回错误代码,以便 RP 可以使用
mediation: required
再次调用 FedCM API。
因此,提供自动重新身份验证流程的可见性对开发者有益。
使用 Auto-selected Flag API,Chrome 会在每次发生自动重新身份验证或发生明确的中介时,与 IdP 和 RP 分享是否通过点按继续以身份登录按钮获得了明确的用户权限。只有在获得 IdP/RP 通信的用户权限后,才会进行共享。
IdP 共享
为了将信息分享给 IdP 的用户权限后,Chrome 会在发送给 id_assertion_endpoint
的 POST
请求中添加 is_auto_selected=true
:
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=Ct0D&disclosure_text_shown=true&is_auto_selected=true
RP 共享
浏览器可以通过 IdentityCredential
将信息分享给 isAutoSelected
中的 RP:
const cred = await navigator.credentials.get({
identity: {
providers: [{
configURL: 'https://idp.example/manifest.json',
clientId: '1234'
}]
}
});
if (cred.isAutoSelected !== undefined) {
const isAutoSelected = cred.isAutoSelected;
}
互动和分享反馈
如果您有反馈或在测试期间遇到任何问题,可以通过 crbug.com 分享。
照片提供者:Unsplash 用户 Girl with red hat