Google I/O 大会专题演讲安全、无缝登录:持续吸引用户中介绍了一些相关更新:
Chrome 57
Chrome 57 对 Credential Management API 引入了这项重要变更。
可通过其他子网域共享凭据
Chrome 现在可以使用 Credential Management API 检索存储在不同子网域中的凭据。例如,如果密码存储在 login.example.com
中,则 www.example.com
上的脚本可将它显示为帐号选择器对话框中的帐号项之一。
您必须使用 navigator.credentials.store()
明确存储密码。这样一来,当用户通过点按对话框选择凭据时,密码就会传递并复制到当前源站。
存储后,密码将作为凭据从完全相同的来源 www.example.com
开始提供。
在下面的屏幕截图中,存储在 login.aliexpress.com
下的凭据信息对 m.aliexpress.com
可见,并可供用户选择:
Chrome 60
Chrome 60 对 Credential Management API 引入了几项重要变更:
由于在提取密码时不再需要自定义
fetch()
函数,因此该函数很快就会被弃用。navigator.credentials.get()
现在接受枚举mediation
,而不是布尔标志unmediated
。新方法
navigator.credentials.create()
异步创建凭据对象。
特征检测需要注意
如需了解用于访问基于密码的凭据和联合凭据的 Credential Management API 是否可用,请检查 window.PasswordCredential
或 window.FederatedCredential
是否可用。
if (window.PasswordCredential || window.FederatedCredential) {
// The Credential Management API is available
}
PasswordCredential
对象现在包含密码
Credential Management API 采用保守的方法处理密码。它从 JavaScript 中隐藏了密码,从而要求开发者通过 fetch()
API 的扩展程序直接将 PasswordCredential
对象发送到其服务器进行验证。
但这种方法引入了许多限制。我们收到了反馈称,开发者无法使用该 API,原因如下:
他们必须将密码作为 JSON 对象的一部分发送。
他们必须将密码的哈希值发送到其服务器。
在进行了安全分析并确定在 JavaScript 中隐藏密码并不会像我们希望的那样有效地阻止所有攻击途径,我们决定做出一项改变。
现在,Credential Management API 会在返回的凭据对象中包含原始密码,以便您以纯文本形式访问该凭据。您可以使用现有方法向服务器传递凭据信息:
navigator.credentials.get({
password: true,
federated: {
providers: [ 'https://accounts.google.com' ]
},
mediation: 'silent'
}).then(passwordCred => {
if (passwordCred) {
let form = new FormData();
form.append('email', passwordCred.id);
form.append('password', passwordCred.password);
form.append('csrf_token', csrf_token);
return fetch('/signin', {
method: 'POST',
credentials: 'include',
body: form
});
} else {
// Fallback to sign-in form
}
}).then(res => {
if (res.status === 200) {
return res.json();
} else {
throw 'Auth failed';
}
}).then(profile => {
console.log('Auth succeeded', profile);
});
自定义提取功能即将被弃用
如需确定您是否使用自定义 fetch()
函数,请检查该函数是使用 PasswordCredential
对象还是 FederatedCredential
对象作为 credentials
属性的值,例如:
fetch('/signin', {
method: 'POST',
credentials: c
})
建议使用常规 fetch()
函数(如前面的代码示例所示)或 XMLHttpRequest
。
navigator.credentials.get()
现在接受枚举中介
在 Chrome 60 之前,navigator.credentials.get()
接受带有布尔标志的可选 unmediated
属性。例如:
navigator.credentials.get({
password: true,
federated: {
providers: [ 'https://accounts.google.com' ]
},
unmediated: true
}).then(c => {
// Sign-in
});
设置 unmediated: true
可防止浏览器在传递凭据时显示帐号选择器。
该标志现已扩展为中介。 在以下情况下,系统可能会触发用户中介:
用户需要选择一个账号来登录。
用户希望在调用
navigator.credentials.requireUseMediation()
后明确登录。
为 mediation
值选择以下选项之一:
mediation 值 |
与布尔标志进行比较 | 行为 | |
---|---|---|---|
silent |
等于 unmediated: true |
已通过凭据验证,但未显示账号选择器。 | |
optional |
等于 unmediated: false |
之前调用 preventSilentAccess() ,则显示帐号选择器。 |
|
required |
新选项 | 始终显示帐号选择器。 如果您希望允许用户使用原生帐号选择器对话框切换帐号,这会非常有用。 |
在此示例中,凭据在传递时不会显示帐号选择器(等同于前一个标志 unmediated: true
):
navigator.credentials.get({
password: true,
federated: {
providers: [ 'https://accounts.google.com' ]
},
mediation: 'silent'
}).then(c => {
// Sign-in
});
requireUserMediation()
已重命名为 preventSilentAccess()
为了与 get()
调用中提供的新 mediation
属性完美保持一致,navigator.credentials.requireUserMediation()
方法已重命名为 navigator.credentials.preventSilentAccess()
。
重命名后的方法会阻止在不显示帐号选择器的情况下传递凭据(有时无需用户中介即可调用)。当用户退出网站或取消注册网站,并且不希望在下次访问时自动重新登录时,此功能非常有用。
signoutUser();
if (navigator.credentials) {
navigator.credentials.preventSilentAccess();
}
使用新方法 navigator.credentials.create()
异步创建凭据对象
您现在可以选择使用新方法 navigator.credentials.create()
异步创建凭据对象。下文就同步方法与异步方法进行了比较。
创建 PasswordCredential
对象
同步方式
let c = new PasswordCredential(form);
异步方法(新)
let c = await navigator.credentials.create({
password: form
});
或:
let c = await navigator.credentials.create({
password: {
id: id,
password: password
}
});
创建 FederatedCredential
对象
同步方式
let c = new FederatedCredential({
id: 'agektmr',
name: 'Eiji Kitamura',
provider: 'https://accounts.google.com',
iconURL: 'https://*****'
});
异步方法(新)
let c = await navigator.credentials.create({
federated: {
id: 'agektmr',
name: 'Eiji Kitamura',
provider: 'https://accounts.google.com',
iconURL: 'https://*****'
}
});
迁移指南
已有 Credential Management API 实现?您可以参照我们的迁移指南文档升级到新版本。