能力 · API
Credential Management API
发布于 更新于
Credential Management API 就是 navigator.credentials 这个容器:页面通过它向浏览器的凭据存储索取一个 Credential、交回一个让浏览器保存、新建一个,或者告诉浏览器不要再静默登录。五种凭据类型接入这个容器:PasswordCredential、PublicKeyCredential(WebAuthn)、OTPCredential、IdentityCredential(FedCM),以及已被取代的 FederatedCredential。
容器本身历史久、覆盖广:CredentialsContainer、get() 与 store() 见于 Chrome 51、Edge 18、Firefox 60 与 Safari 13;create() 见于 Chrome 60;preventSilentAccess() 见于 Chrome 60(Chrome 51 至 59 叫 requireUserMediation())、Edge 18、Firefox 60 与 Safari 17,而 Safari 13 至 16 暴露了该方法却对每次调用都以 NotSupportedError 拒绝(BCD api.CredentialsContainer)。凭据类型则窄得多:PasswordCredential 只存在于 Chrome 51,Firefox 与 Safari 任何版本都没有;get({ otp }) 见于 Chrome 93(Android 上 Chrome 84)与 Safari 27;get({ identity }) 见于 Chrome 108 与 Safari 27。本页讲容器;PublicKeyCredential 有独立条目。
navigator.credentials.get()navigator.credentials.get(options)navigator.credentials.store(credential)navigator.credentials.create()navigator.credentials.create(options)navigator.credentials.preventSilentAccess()get() 与 create() 返回 Promise<Credential?>:一个 Credential 子类实例,或者在 mediation 允许的用户介入程度内拿不到、建不出任何凭据时为 null。store() 与 preventSilentAccess() 返回 Promise<undefined>。四个方法都带 [SecureContext];在 localhost 以外的 http:// 源上容器为 undefined。
get() 接受 CredentialRequestOptions,create() 接受 CredentialCreationOptions,store() 接受一个 Credential,preventSilentAccess() 不接受参数。每种凭据类型往两个字典里各加一个成员;一次请求通过包含哪些成员来声明它接受哪些类型。
| 成员 | 类型 | 必填 | 说明 |
|---|---|---|---|
mediation(get、create) |
"silent"、"optional"、"conditional" 或 "required" |
否 | 默认 "optional"。"silent" 不显示任何界面,直接以 null 兑现;"optional" 只在必要时显示选择器;"conditional" 把发现的凭据放进非模态界面(自动填充),并且不会很快以 null 兑现;"required" 忽略任何静默访问的许可,必定提示。对 create(),"conditional" 允许刚经过用户介入的登录免模态创建,否则拒绝。 |
uiMode(get) |
DOMString |
否 | "immediate":有凭据则显示模态选择器,否则立即以 null 完成且不提示。 |
signal(get、create) |
AbortSignal |
否 | 中止时以该 signal 的 reason 拒绝(默认 AbortError,AbortSignal.timeout() 给出 TimeoutError),除非操作已经完成。 |
password(get) |
boolean |
否 | 默认 false。true 表示索取已保存的 PasswordCredential。 |
password(create) |
PasswordCredentialData 或 HTMLFormElement |
否 | PasswordCredentialData 含 id(必填)、origin(必填)、password(必填)、name 与 iconURL;传表单时按其 autocomplete 标记读取。 |
federated(get、create) |
FederatedCredentialRequestOptions / FederatedCredentialInit |
否 | providers: sequence<USVString> 与 protocols: sequence<DOMString>,用于已被取代的 FederatedCredential。 |
publicKey(get、create) |
PublicKeyCredentialRequestOptions / PublicKeyCredentialCreationOptions |
否 | WebAuthn;见「WebAuthn 与通行密钥」条目。 |
otp(get) |
OTPCredentialRequestOptions |
否 | transport: ["sms"];兑现一个 OTPCredential,其 code 为短信一次性验证码。 |
identity(get) |
IdentityCredentialRequestOptions |
否 | providers 内含 configURL、clientId 及可选的 nonce、loginHint、domainHint;FedCM。 |
传给 store() 的 credential 必须是页面从 get() 得到的或自行构造的 Credential(new PasswordCredential(data) 或 new PasswordCredential(form))。
算法按固定顺序拒绝。凭据类型自身内部方法抛出的任何拒绝(例如 WebAuthn 超时的 NotAllowedError)原样透传。
| 异常 | 条件 |
|---|---|
InvalidStateError |
文档不是 fully active(四个方法都适用)。Chromium 还在同一容器上另有请求进行中时使用它。 |
NotSupportedError |
get() 没有任何类型成员,或两个成员的类型不能共用一次请求;create() 带了多于一个类型成员,或没有关联文档。Safari 13 至 16 对每次 preventSilentAccess() 调用都以它拒绝,Chromium 在密码存储不可用时也以它拒绝。 |
SecurityError |
从不透明源请求、创建或保存绑定源的类型(PasswordCredential、FederatedCredential)。 |
TypeError |
对不支持的类型使用 mediation: "conditional" 或 uiMode: "immediate";create({ password }) 的数据或表单无法构造出 PasswordCredential。 |
NotAllowedError |
请求的类型在当前 settings object 中已处于活动状态;该类型的 Permissions Policy 拒绝了文档;在与所有祖先不同源的上下文中收集或保存 PasswordCredential;没有事先同意就以 mediation: "conditional" 调用 create()。 |
| signal 的中止原因 | get() 或 create() 运行时 options.signal 已被中止,或在操作完成前被触发。 |
即使浏览器没有保存任何凭据,preventSilentAccess() 也会兑现;规范为它定义的唯一拒绝是 InvalidStateError。
示例除了检测 navigator.credentials,还检测 PasswordCredential,因为 Firefox 60+ 与 Safari 13+ 提供容器却没有这个类型:'credentials' in navigator 为真,而 get({ password: true }) 永远以 null 兑现(BCD api.PasswordCredential)。
进站时静默登录,带超时与可见的回退
Section titled “进站时静默登录,带超时与可见的回退”mediation: "silent" 不显示选择器;需要用户同意时以 null 兑现,页面因此可以显示普通的登录按钮,而不是一个没人要的对话框。AbortSignal.timeout() 把卡住的存储变成可捕获的 TimeoutError。
async function trySilentSignIn() { if (!('credentials' in navigator) || !('PasswordCredential' in window)) { showSignInButton(); return; } try { const cred = await navigator.credentials.get({ password: true, mediation: 'silent', signal: AbortSignal.timeout(5000), }); if (!cred) return showSignInButton(); await postJson('/session', { id: cred.id, password: cred.password }); showSignedInState(cred.name ?? cred.id); } catch (err) { if (err.name === 'TimeoutError' || err.name === 'AbortError') return showSignInButton(); throw err; }}
window.addEventListener('load', trySilentSignIn);TimeoutError 与 AbortError 都要分支处理:AbortSignal.timeout() 以前者拒绝,手动的 AbortController 以后者拒绝。
表单登录成功后记住密码
Section titled “表单登录成功后记住密码”服务器接受凭据之后调用 store(),下次访问的静默 get() 才可能成功。用表单构造 PasswordCredential,让 autocomplete="username" 与 autocomplete="current-password" 字段提供 id 和 password。
document.querySelector('#sign-in').addEventListener('submit', async (event) => { event.preventDefault(); const form = event.currentTarget; const ok = await postForm('/session', new FormData(form)); if (!ok) return showError('用户名或密码错误');
if ('PasswordCredential' in window) { try { await navigator.credentials.store(new PasswordCredential(form)); } catch (err) { console.warn(`store() failed: ${err.name}`); // 登录本身已经成功 } } location.assign('/home');});只有 Chrome 51+ 会走进这个分支;在 Firefox 与 Safari 中表单正常提交,由浏览器自带的密码管理器提议保存。
登出时清除静默访问,兼顾 Safari 13 至 16
Section titled “登出时清除静默访问,兼顾 Safari 13 至 16”preventSilentAccess() 设置该源的标志,下一次 get() 就必须经过用户介入。在 Safari 13 至 16 上 typeof 检查能通过,但每次调用都以 NotSupportedError 拒绝,所以只捕获这一个名称并视为完成。
async function signOut() { await fetch('/session', { method: 'DELETE' }); if (typeof navigator.credentials?.preventSilentAccess === 'function') { try { await navigator.credentials.preventSilentAccess(); } catch (err) { if (err.name !== 'NotSupportedError') throw err; } } location.assign('/');}只吞掉 NotSupportedError;文档已分离导致的 InvalidStateError 仍会抛出。
- WebAuthn 与通行密钥(passkeys),本页留给它的
PublicKeyCredential类型 - Payment Request API,这类敏感步骤前适合先用
mediation: "required" - Web app manifest id:PWA 的稳定身份,已安装应用自身的身份,而非用户的身份
- Credential Management Level 1: Request a Credential(w3.org)
- The Credential Management API(web.dev)
- Chrome Platform Status: Credential Management API(chromestatus.com)
规范
| 规范 | 状态 |
|---|---|
| Credential Management Level 1: get() method | W3C |
| Credential Management Level 1: preventSilentAccess() method | W3C |