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 中提供。
在以下屏幕截图中,m.aliexpress.com 可以看到存储在 login.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'
}).the>n(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&>#39;
}).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: 39;https://*****'
}
});
迁移指南
已经实现了 Credential Management API?我们提供了迁移指南文档,您可以按照该文档升级到新版本。