凭证管理 一级

W3C 工作草案,

关于本文档的更多详情
此版本:
https://www.w3.org/TR/2026/WD-credential-management-1-20260903/
最新发布版本:
https://www.w3.org/TR/credential-management-1/
编辑草案:
https://w3c.github.io/webappsec-credential-management/
之前版本:
历史记录:
https://www.w3.org/standards/history/credential-management-1/
反馈:
public-webappsec@w3.org 主题为 “[credential-management] … 消息主题 …” (存档)
GitHub
编辑:
(Google Inc.)
(Apple Inc.)
前编辑:
(Google Inc.)
(Google Inc.)
参与:
提交问题 (开放的问题)

摘要

本规范描述了一个命令式 API,使网站能够向用户代理请求用户的凭证,并帮助用户代理正确地存储用户凭证以备将来使用。

本文档状态

本节说明了本文件在发布时的状态。当前的 W3C 出版物列表以及本技术报告的最新修订可在 W3C 技术报告索引 中找到。

本文件由 Web 应用安全工作组 作为工作草案发布,使用推荐轨道。本文件旨在成为一项 W3C 推荐。

(已归档)公共邮件列表 public-webappsec@w3.org (参见 说明) 是讨论本规范的首选渠道。 发送电子邮件时, 请在主题中写入“credential-management”字样, 最好像这样: “[credential-management] …评论摘要…”

将其作为工作草案发布并不意味着 W3C 及其成员对此的认可。本文件为草稿文档,可能随时被更新、替换或被其他文档废止。不应将本文件作为除进行中工作之外的正式引用。

本文件由 Web 应用安全工作组 制作。

本文件由在 W3C 专利政策 下运作的小组制作。 W3C 维护了一个与该小组交付物相关的任何专利披露的公开列表; 该页面也包含披露专利的说明。 个人如果确有专利知识并认为该专利包含实质性权利要求(Essential Claim(s)), 必须按照 W3C 专利政策第6节 的规定披露相关信息。

本文件受 2025年8月18日 W3C 流程文档 的管辖。

1. 简介

本节为非规范性内容。

登录网站比应有的要困难得多。用户代理处于一个独特的位置,能够在多方面改善这种体验,大多数现代用户代理已经通过在浏览器中原生提供某种程度的 credential management 来认识到这一点。例如,用户可以为网站保存用户名和密码;那些 凭证 随后会自动填充到登录表单中,尽管成功率各不相同。

属性 autocomplete 提供了一种声明性机制,网站可以通过它与用户代理协作——通过将特定字段标记为 "username" 或 "password"——以提高后者检测并填写登录表单的能力;用户代理还实现了各种检测启发式方法,以便与那些没有花时间在标记中提供这些细节的网站配合。

虽然这种启发式与声明式检测的组合运行得相对较好,但现状仍然存在一些检测困难的重大空白。具有不常见登录机制的网站(例如通过 XMLHttpRequest 提交凭证)很难可靠检测,用户希望使用联合身份提供者进行认证的情况也越来越常见。允许网站更直接地与用户代理的 凭证管理器 交互,一方面可以使 凭证管理器 更加准确,另一方面也能帮助用户进行联合登录。

这些用例在 § 1.1 Use Cases 和 Credential Management: Use Cases and Requirements 中有更详细的探讨;本规范通过定义一个凭证管理器 API(Credential Manager API),尝试解决该文档概述的许多需求,网站可以使用该 API 为用户请求 凭证,并在用户成功登录时请求用户代理持久化这些凭证。

注意: 这里定义的 API 有意保持小而简单:它并不打算自身提供认证功能,而仅限于向现有用户代理实现的 凭证管理器 提供一个接口。该功能在 现在 就已很有价值,而不需要供应商或作者付出大量额外努力。当然,仍有许多可以做的事情。参见 § 9 Future Work,其中列出了一些我们暂时搁置、但可在未来 API 迭代中继续探讨的想法。

1.1. 使用场景

现代用户代理通常都为用户在登录网站时提供保存密码的能力,并且在用户再次访问网站时能全自动或半自动地填充这些密码。从网站的角度来看,这一行为完全不可见:网站不知道密码已被存储,也不会收到密码被填充的通知。这既有好处,也有不足。一方面,用户代理的密码管理器无论网站是否配合都能工作,这对用户来说非常好。另一方面,密码管理器的行为是一套脆弱且专有的启发式组合,旨在检测和填充登录表单、密码更改表单等。

现状中有几个突出问题值得关注:

2. 核心 API

从开发者的角度来看,凭证是一个对象,允许开发者为特定操作做出认证决策。本节定义了一个通用且可扩展的 Credential 接口,作为本规范及其他文档中定义的凭证的基类,并提供一组挂载在 navigator.credentials.* 上的 API,供开发者获取凭证。

不同的 凭证类型 都以接口的形式呈现给 JavaScript,这些接口直接或间接地继承自 Credential 接口。本规范定义了两个此类接口:PasswordCredential 和 FederatedCredential。其他规范,如 [WEBAUTHN],定义了其他凭证类型。

如果一个凭据 在某个特定源上被接受用于身份验证,则该凭据对于该源是 有效的。即使一个凭据在某个特定 时间点是有效的,UA 也不能假定同一个凭据在未来任何时间仍然有效, 原因有以下几点:

  1. 如果账户持有人更改了密码,密码凭证可能会变为无效。

  2. 通过短信收到的令牌生成的凭证通常只在一次使用时有效。

一次性 凭证由 凭证源生成,凭证源可能是私钥、访问联合账户、或某个手机号接收短信的能力等。凭证源不会暴露给 Javascript,也不会在本规范中明确表示。为统一模型,我们把密码视为一种凭证源,通过复制生成密码凭证。

虽然 UA 不能假定一个有效凭证再次使用时仍然有效,也不能假定生成过有效凭证的凭证源未来能够再次生成有效凭证,但后者的可能性更大。通过记录(使用 store()) 过去哪些凭证是有效的,UA 能更好地在将来为用户提供有效的凭证源选择。

2.1. 基础设施

凭证管理器是一种应用程序、硬件设备或服务,用于存储、组织、管理并允许选择凭证。典型的凭证管理器包括数字钱包、密码管理器,以及passkey管理器。

用户代理必须在内部提供一个凭证存储,它是一种供应商特定的不透明存储机制,用于记录哪些凭证已被激活。它为凭证的访问和持久化提供以下功能:

  1. 存储凭证以便后续检索。此操作接受一个凭证,并将其插入凭证存储中。

  2. 检索凭证列表。此操作接受任意筛选条件,并返回与筛选条件匹配的一组凭证。

  3. 修改凭证。此操作接受一个凭证,并覆盖已有凭证在凭证存储中的状态。

此外,凭证存储应为源维护一个prevent silent access 标志(除非另有说明,默认设为true)。如果该标志为true,则该源需要用户介入。

Note: 关于用户介入的重要性,详见§ 5 用户介入。

Note: 凭证存储是用户代理实现本文档所述 API 的内部实现细节,不会直接暴露给 Web。其他文档可能会为特定凭证类型规定更多能力。

本文档在其算法和正文中使用的一些基础概念依赖 Infra Standard [INFRA]。

每个环境设置对象都有一个相关联的激活凭证类型,它是一个最初为空的集合。

2.1.1. 基础设施算法

2.1.1.1. 与其祖先同源

当 环境设置对象 (settings) 满足以下算法返回 true 时, 称其为 与其祖先同源:

  1. 如果 settings 的 相关全局对象没有 关联的 Document,返回 false。

  2. 令 document 为 settings 的 相关全局对象的 关联的 Document。

  3. 如果 document 没有 浏览上下文,返回 false。

  4. 令 origin 为 settings 的 源。

  5. 令 navigable 为 document 的 节点可导航对象。

  6. 当 navigable 有非空 父级 时:

    1. 令 navigable 为 navigable 的 父级。

    2. 如果 navigable的 活动文档的 源与 同源关系中的 origin 不同源, 返回 false。

  7. 返回 true。

2.1.2. 凭证类型注册表

该注册表将凭证类型(即 [[type]] 的值)映射为与指定凭证类型相关的各种值。例如:选项成员标识符 (正式来说,是 字典成员的标识符),用于 CredentialCreationOptions 和 CredentialRequestOptions (即“选项字典”)的规范。

注意: 此注册表被 相关凭证接口对象算法使用。

凭证类型
(按字母顺序)
选项成员标识符 适用接口对象 获取权限策略 创建权限策略 同一get()请求中允许的类型 规范文档 请求方联系信息
digital-credential digital DigitalCredential digital-credentials-get digital-credentials-create empty [DIGITAL-CREDENTIALS] W3C
federated federated FederatedCredential null null password 本规范:§ 4 联合凭证 W3C
identity identity IdentityCredential identity-credentials-get null empty [FEDCM] W3C
otp otp OTPCredential otp-credentials null empty [WEB-OTP] WICG
password password PasswordCredential null null federated 本规范:§ 3 密码凭证 W3C
public-key publicKey PublicKeyCredential publickey-credentials-get publickey-credentials-create empty [WEBAUTHN] W3C
2.1.2.1. 注册项要求与更新流程

对本注册表的更新包括对某凭证类型条目的新增、修改或删除。 任何人都可以通过向 webappsec-credential-management 仓库提交pull request来请求更新。 Web应用安全工作组会将请求列入即将召开的会议议程, 并通知请求人。 W3C Web应用安全工作组通过共识进行讨论和决定。 主席随后会通知请求人结果,并相应更新注册表。

2.2. Credential 接口

[Exposed=Window, SecureContext]
interface Credential {
  readonly attribute USVString id;
  readonly attribute DOMString type;
  static Promise<boolean> isConditionalMediationAvailable();
};
id, 类型为 USVString,只读

凭据的标识符。每种 凭据 类型的标识符要求是不同的。例如,对于用户名/密码元组,它可能代表用户名。

type, 类型为 DOMString,只读

该属性的 getter 返回对象的 接口对象的 [[type]] 槽位的值,该槽位指定此对象所表示的 凭据类型。

isConditionalMediationAvailable()

返回一个 Promise ,当且仅当用户代理支持 conditional 方式进行 凭据请求调解 且针对 凭据类型 时,该 Promise 会 解析为 true,否则为 false。

Credential 的默认 isConditionalMediationAvailable() 实现如下:

  1. 返回 以 false 解析的 Promise。

任何支持 conditional 调解的 凭据类型 规范,必须显式重写该函数,使其 解析为 true。

注意:如果此函数不存在, conditional 调解对于该 凭据类型 不受支持。

[[type]]

Credential 的 接口对象 有一个名为 [[type]] 的内部槽, 其中存有一个 表示凭据类型的字符串。除非另有说明,否则该槽的值为空字符串。详见 § 2.1.2 凭据类型注册表 列表 凭据类型。

注意: [[type]] 槽的值对于实现某一接口的所有凭据是相同的,这意味着开发者可以依赖 obj.type 返回一个明确表示其具体 Credential 类型的字符串。

[[discovery]]

Credential 的 接口对象 具有一个名为 [[discovery]] 的内部槽, 表示用户代理用于收集给定类型凭据的机制。其值为 "credential store" 或 "remote"。前者意味着所有可用的凭据信息都存储在用户代理的 凭据存储 中,后者意味着用户代理可通过与外部设备或服务交互,在 凭据存储 外发现凭据。

和 Tobie/Dominic 讨论下接口对象相关内容,这里以及 § 2.5.1 请求凭据等处。我不确定术语是否准确。也许应该是 接口原型对象?

某些 Credential 对象是绑定至源的:这些对象包含一个名为 [[origin]] 的内部槽,用于存储该 源 , 该 Credential 可能被 生效 的源。

某些凭据类型是源绑定的:一个凭据属于一个源, 并且只有该源可以使用它。例如,PasswordCredential 只能用于存储它的站点。

如果文档在没有 allow-same-origin 的情况下被沙盒化,或者它来自 data: URL,则该文档具有不透明源。不透明源序列化为 null,并且 无法从中重新创建 任何内容,因此不存在可将凭据绑定到的源。因此,从此类文档执行的涉及源绑定凭据的操作 会被拒绝,并抛出 "SecurityError" DOMException。

2.2.1. Credential 内部方法

Credential 接口对象具有若干 内部方法, 用于 检索和存储 Credential 对象,其默认实现为“空操作”, 如本节下文所述。

除非另有规定,每个为 继承自 Credential 的接口创建的 接口对象 必须至少为这些内部方法之一提供实现,覆盖 Credential 的默认实现,具体应依据对应的 凭据类型。 例如,§ 3.2 PasswordCredential 接口、 § 4.1 FederatedCredential 接口 以及 [WEBAUTHN]。

2.2.1.1. [[CollectFromCredentialStore]] 内部方法
[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 被调用时,传入一个 源 origin、一个 CredentialRequestOptions 对象,以及一个布尔值,仅当调用方的 环境设置对象与其 祖先同源时才为 true。 该算法会从用户代理的 凭据存储 中返回一组与所提供 options 匹配的 Credential 对象。 如果没有可用的匹配 Credential 对象, 返回的集合将为空。

Credential 的 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 默认实现如下:

  1. 返回空集合。

2.2.1.2. [[DiscoverFromExternalSource]] 内部方法
[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 会并行调用,参数为一个 origin、一个 CredentialRequestOptions 对象,以及一个布尔值,仅当调用方的 环境设置对象与其 祖先同源时为 true。 如果能够根据所提供的 options 返回一个 Credential ,则返回该对象;若没有凭据可用,则返回 null;如果发现失败(例如 options 参数错误会产生一个 TypeError),则抛出异常。 如果此类 Credential 仅对单次使用或仅在有限时间内 生效,该方法负责使用 凭据源生成新的 凭据。

Credential 的 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 默认实现如下:

  1. 返回 null。

2.2.1.3. [[Store]] 内部方法
[[Store]](credential, sameOriginWithAncestors) 会并行调用,参数为一个 Credential 和一个布尔值,仅当调用方的 环境设置对象与其 祖先同源时为 true。 当 Credential 被持久化到 凭据存储 后,该算法返回。

Credential 的 [[Store]](credential, sameOriginWithAncestors) 默认实现如下:

  1. 抛出 NotSupportedError。

2.2.1.4. [[Create]] 内部方法
[[Create]](origin, options, sameOriginWithAncestors) 会并行调用,参数为一个 origin,一个 CredentialCreationOptions ,以及一个布尔值,仅当调用方的 环境设置对象 与其祖先同源时为 true。 该算法会:

当创建 Credential 时,会返回一个算法,该算法接收一个 全局对象 并返回一个继承自 Credential 的 接口对象。该算法必须从一个任务中调用。

注意:该算法的步骤由每种凭据类型单独定义。

Credential 的 [[Create]](origin, options, sameOriginWithAncestors) 默认实现如下:

  1. 返回 null。

2.2.2. CredentialUserData 混入

某些 Credential 对象包含旨在为用户在 凭据选择器 中提供友好名称和图标,以实现人类可读的区分机制的数据:

[SecureContext]
interface mixin CredentialUserData {
  readonly attribute USVString name;
  readonly attribute USVString iconURL;
};
name, 类型为 USVString,只读

与凭据关联的名称,旨在作为公示名称在人类可读的 凭据选择器 中显示。

iconURL, 类型为 USVString,只读

指向凭据图像的 URL,旨在 凭据选择器 中显示。此 URL 必须是 潜在可信 URL。

2.3. navigator.credentials

开发者通过暴露在 CredentialsContainer 接口上的方法获取 Credential 并与用户代理的 凭据存储 交互,该接口作为 Navigator 对象的一个属性挂载在 navigator.credentials 上。

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute CredentialsContainer credentials;
};

credentials 属性必须返回与 活动文档 的 浏览上下文 关联的 CredentialsContainer。

注意: 如 § 6.3 不安全站点 所述,凭据管理 API 仅暴露在 安全上下文(Secure Context) 中。

[Exposed=Window, SecureContext]
interface CredentialsContainer {
  Promise<Credential?> get(optional CredentialRequestOptions options = {});
  Promise<undefined> store(Credential credential);
  Promise<Credential?> create(optional CredentialCreationOptions options = {});
  Promise<undefined> preventSilentAccess();
};

dictionary CredentialData {
  required USVString id;
};
get(options)

当调用get() 时,用户代理必须返回 执行请求Credential 的结果,参数为 options。

CredentialsContainer.get(options) 方法参数。
参数 类型 可为 null 可选 描述
options CredentialRequestOptions ✘ ✔ 控制请求范围的属性集。
store(credential)

当调用store() 时,用户代理必须返回 执行存储Credential 的结果,参数为 credential。

CredentialsContainer.store(credential) 方法参数。
参数 类型 可为 null 可选 描述
credential Credential ✘ ✘ 要存储的凭证。
create(options)

当调用create() 时,用户代理必须返回 执行创建Credential 的结果,参数为 options。

CredentialsContainer.create(options) 方法参数。
参数 类型 可为 null 可选 描述
options CredentialCreationOptions ✘ ✔ 用于创建Credential的选项。
preventSilentAccess()

当调用preventSilentAccess() 时,用户代理必须返回 执行禁止静默访问算法,传入当前设置对象。

注意: 这里的意图是在于由来源发出用户已登出的信号。 也就是说,在用户点击“登出”按钮后,站点会更新用户会话信息, 并调用navigator.credentials.preventSilentAccess()。 这会设置禁止静默访问标志, 表示下次用户访问页面时不会自动将凭证返回给页面。

注意: 该方法之前叫 requireUserMediation(),现已弃用。

当创建一个Navigator 对象(navigator)时,用户代理必须创建一个 新的CredentialsContainer 对象,使用navigator的相关 Realm,并将其关联到navigator。

2.3.1. CredentialRequestOptions 字典

为了通过get() 获取一个Credential, 调用方需要在一个CredentialRequestOptions 对象中指定一些参数。

注意:CredentialRequestOptions 字典是一个扩展点。如果将来引入了需要选项的新凭证类型,其字典类型也会添加到此字典中,进而可以传递到请求中。参见§ 8.2 扩展点。

dictionary CredentialRequestOptions {
  CredentialMediationRequirement mediation = "optional";
  DOMString uiMode;
  AbortSignal signal;
};
mediation, 类型为 CredentialMediationRequirement, 默认值为 "optional"

此属性指定特定凭证请求的调解(mediation)要求。每个枚举值的含义见下文 CredentialMediationRequirement。 处理细节定义见§ 2.5.1 请求Credential。

uiMode, 类型为 DOMString

此属性指定在用户代理执行用户介入(user mediation)时, 对于特定凭证请求采用哪种界面模式。每个值的含义见下文 CredentialUiMode。 处理细节见§ 2.5.1 请求 Credential。

signal, 类型为 AbortSignal

此属性允许开发者中止正在进行的get() 操作。 被中止的操作可能会正常完成(通常指在操作结束后才收到中止请求的情况)或者以中止原因拒绝(reject)。

早期版本的规范定义了一个布尔类型的unmediated成员。将其设为true等价于设置 mediation 为 "silent", 设为false则等价于 设置mediation 为"optional"。

unmediated应视为已废弃;新代码应改用 mediation。

给定 CredentialCreationOptions 或CredentialRequestOptions (options)的 相关凭证接口对象 是按如下方式收集的一组接口对象:

注意:本算法使用凭证类型注册表。

  1. 令settings为 当前设置对象。

  2. 令relevant interface objects为 空 集合。

  3. 对于每一个 optionKey → optionValue 属于 options:

    1. 令credentialInterfaceObject为 (在settings的 全局对象上) 适用接口对象, 其选项成员标识���为 optionKey。

    2. 断言:credentialInterfaceObject的 [[type]] 槽的值等于 凭证类型, 该类型的 选项成员标识符为 optionKey。

    3. 将credentialInterfaceObject添加到relevant interface objects。

  4. 返回relevant interface objects。

给定的CredentialRequestOptions (options) a priori 可匹配(matchable a priori), 如果通过如下步骤得到true:
  1. 对于options的 相关凭证接口对象 中的每个interface:

    1. 如果interface的 [[discovery]] 槽的值不是 "credential store", 返回false。

  2. 返回true。

注意: 执行get(options) 时,只有在给定的CredentialRequestOptions 为a priori 可匹配时, 我们才返回无需用户介入的凭证。如果请求了可能需要来自外部服务发现的凭证类型 (如OAuth令牌、安全密钥认证器等),则需要用户介入以引导发现流程(如选择联合身份提供者、BTLE 设备等)。

2.3.2. 调解要求

通过get(options) 或create(options) 进行请求时,开发者可通过选择合适的 CredentialMediationRequirement 枚举值,逐个请求地设置是否需要 用户介入。

注意: § 5 用户介入一节将在整体上进行详细说明, 并介绍其对用户代理如何处理某个源的独立请求的影响。

enum CredentialMediationRequirement {
  "silent",
  "optional",
  "conditional",
  "required"
};
silent

对于该操作,抑制用户介入。如果操作无需用户参与即可完成,则顺利进行。 如果必须用户参与,则该操作会返回null,而不是打扰用户。

注意: 用于支持 “保持我登录状态”场景, 即开发者希望在用户可自动登录时静默获取凭证,只有在用户主动发起登录时才弹出提示。

optional

如果可以无需用户介入就交付凭证,则会这样交付。若 需要用户介入,则用户代理会让用户参与决策。

注意: 这是 get() 的默认行为,适用于开发者有合理信心用户希望开始登录操作的情形。 例如用户刚点击“登录”按钮时,看到 凭证选择器 也不会感到奇怪或困惑。

conditional

对于 get(), 发现的凭据会连同请求凭据的 源的指示一起显示在非模态对话框中。如果用户 在对话框 外做出手势,对话框将关闭,而不会兑现或拒绝由 Promise get() 方法返回的内容,也不会导致用户可见的 错误状态。如果用户做出选择某个凭据的手势,则该凭据会 返回给调用者。阻止静默访问标志会 被视为 true, 无论其实际值如何:conditional 行为 如果发现适用的凭据,总是涉及某种形式的 用户中介 。

如果没有发现凭证,用户代理可以根据凭证类型提示用户采取操作 (例如插入包含凭证的设备)。 无论哪种情况,get()方法都不得立刻以 null返回, 以避免向网站暴露缺少可用凭证的事实。

网站只有在所有涉及的凭证接口 都覆盖了isConditionalMediationAvailable(), 并返回 resolve为true的 Promise时, 才能把conditional传入 get()方法。

对于create(), 如果用户曾同意凭证创建且用户代理最近完成过一次身份认证, 则create()调用可在无需额外的模态交互的情况下完成。 如果用户代理最近未完成身份认证或没有凭证创建的同意,则该调用必须抛出 "NotAllowedError" DOMException异常。

required

用户代理若无用户介入,不会交付凭证, 即使该源的 禁止静默访问标志 未设置。

注意: 此要求用于支持重新认证 或切换用户等场景。 该要求仅影响本次操作,不会影响该源的 禁止静默访问标志。 若要设置该标志,开发者应调用preventSilentAccess()。

2.3.3. 界面模式

通过get(options) 发起请求时,开发者可以选择合适的 CredentialUiMode 枚举值来指明期望的用户介入模式。

UI Mode 字段没有默认值。如果未指定, [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)方法必须仅依据 CredentialMediationRequirement 的值来决定浏览器行为。

注: 这允许调用者区分 不同可用形式的 用户中介,当发生 用户中介时。 是否显示 UI 取决于请求的 CredentialMediationRequirement 值。并非 CredentialUiMode 的所有值与 CredentialMediationRequirement 的所有值组合时都合理, 而这种 交互取决于 支持该功能的凭据 类型的 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 方法。

enum CredentialUiMode {
  "immediate"
};
immediate

调用get()时, 用户代理要么向用户显示一个模态的凭证选择对话框, 要么立即完成操作且不返回凭证。

这可以让用户看到精简的登录界面,或者如果没有可用凭证,网站可采取其它操作,比如显示自定义登录页或让用户以未登录状态继续后续流程。

如果没有凭证可在选择对话框中展示,则会跳过用户介入, 例如不会提示用户手动输入凭证。

注意: 本规范未提供类似 isConditionalMediationAvailable() 这样用于检测immediate 能力的静态方法。 将来如果该调解模式扩展到更多凭证类型,则需要一种更通用的特性检测方法,以指明某个 CredentialMediationRequirement 和 CredentialUiMode 组合下支持哪些凭证类型。

2.3.3.1. 示例
MegaCorp公司希望在可能的情况下让用户无缝登录。为此,他们可以在着陆页加载的合适时机, 对所有未登录用户调用 get(), 并传入一个mediation 成员,设为 "silent"。 这确保那些已选择 去除用户介入要求(如§ 5.2 要求用户介入所述)的用户会自动登录, 而未选择此行为的用户则不会莫名被弹出 凭证选择器打扰:
window.addEventListener('load', async () => {
  const credentials = await navigator.credentials.get({
    ...,
    mediation: 'silent'
  });
  if (credentials) {
    // 哇!我们可以用这些凭证直接让用户登录!
  }
});
当用户点击“登录”时,MegaCorp公司希望为用户带来最顺畅的体验。 如果用户选择了无需 用户介入即可登录, 且用户代理能明确选择唯一凭证,很棒! 否则会弹出 凭证选择器。
document.querySelector('#sign-in').addEventListener('click', async () => {
  const credentials = await navigator.credentials.get({
    ...,
    mediation: 'optional'
  });
  if (credentials) {
    // 哇!我们可以用这些凭证直接让用户登录!
  }
});

注意: MegaCorp公司也可以根本不填写 mediation 字段,因为"optional" 本身就是默认值。

MegaCorp公司希望在执行敏感操作前要求用户重新认证。 即使用户选择了 无需用户介入即可登录, MegaCorp公司仍可以通过调用get() 并将mediation 字段设为 "required",确保用户代理一定要求介入:

注意: 根据浏览器或凭证类型的安全模型,可能需要用户用某种方式认证,例如输入主密码、指纹扫描等,才能把凭证交给网站。

document.querySelector('#important-form').addEventListener('submit', async () => {
  const credentials = await navigator.credentials.get({
    ...,
    mediation: 'required'
  });
  if (credentials) {
    // 检查 |credentials| 是否有权限访问,没有权限就取消提交。
  } else {
    e.preventDefault();
  }
});
MegaCorp公司希望支持一次登录多个用户账号。 为确保用户有机会选用不同的凭证, MegaCorp公司可以在点击 “添加账号” 按钮时,调用get(), 并将mediation 字段设为 "required", 这样点击“添加账号”按钮时不会自动返回凭证:
document.querySelector('#switch-button').addEventListener('click', e => {
  var c = await navigator.credentials.get({
    ...,
    mediation: 'required'
  });
  if (c) {
    // 用 |c| 让用户登录。
  }
});

2.4. CredentialCreationOptions 字典

为了通过 create() 创建 Credential ,调解者需要在 CredentialCreationOptions 对象中指定一些参数。

注意: CredentialCreationOptions 字典是一个扩展点。若未来引入新的凭据类型,将会扩展该字典,这样这些新参数可被传递给创建方法。详见 § 8.2 扩展点,以及本文档的扩展接口: § 3.2 PasswordCredential 接口 和 § 4.1 FederatedCredential 接口。

dictionary CredentialCreationOptions {
  CredentialMediationRequirement mediation = "optional";
  AbortSignal signal;
};
signal, 类型为 AbortSignal

该属性允许开发者中止正在进行的 create() 操作。被中止的操作可能会正常完成(如在操作结束后才收到中止),也可能因 中止原因被拒绝。

2.5. 算法

2.5.1. 请求 Credential

请求 Credential 算法 接收一个 CredentialRequestOptions (options)参数,并返回一个 Promise :能唯一获取凭据时 resolve 为 Credential, 否则 resolve 为 null。

  1. 令 settings 为当前设置对象。

  2. 断言:settings 是一个安全上下文。

  3. 令 document 为 settings 的相关全局对象的关联 Document。

  4. 如果 document 不是完全活动的,则返回一个以 "InvalidStateError" DOMException 拒绝的 promise。

  5. 如果 options.signal 已中止, 则返回一个以 options.signal 的 中止原因拒绝的 promise。

  6. 令 interfaces 为 options 的相关 凭据接口对象。

  7. 如果 interfaces 为空,则 返回一个以 "NotSupportedError" DOMException 拒绝的 promise。

  8. 对于 interfaces 中的每个 interface1:

    1. 令 type1 为 interface1 的 [[type]]。

    2. 对于 interfaces 中的每个 interface2:

      1. 令 type2 为 interface2 的 [[type]]。

      2. 如果 type1 等于 type2,则继续。

      3. 如果 type1 的同一个 get() 请求中 允许的类型不包含 type2, 则返回一个以 "NotSupportedError" DOMException 拒绝的 promise。

  9. 令 origin 为 settings 的源。

  10. 对于 interfaces 中的每个 interface:

    1. 如果 interface 的实例是源 绑定的,并且 origin 是 一个不透明源,则返回一个以 "SecurityError" DOMException 拒绝的 promise。

  11. 对于 interfaces 中的每个 interface:

    1. 如果 options.mediation 是 conditional 并且 interface 不 支持 conditional 用户 中介,则返回一个以 TypeError 拒绝的 promise。

    2. 如果 options.uiMode 是 immediate 并且 interface 不 支持 immediate 用户 中介,则返回一个以 TypeError 拒绝的 promise。

    3. 如果 settings 的活动凭据类型包含 interface 的 [[type]], 则返回一个以 "NotAllowedError" DOMException 拒绝的 promise。

    4. 将 interface 的 [[type]] 追加到 settings 的 活动凭据类型中。

  12. 如果 settings 与其祖先同源,则令 sameOriginWithAncestors 为 true,否则为 false。

  13. 对于 options 的相关 凭据接口对象中的每个 interface:

    1. 令 permission 为 interface 的 [[type]] 的Get 权限 策略。

    2. 如果 permission 为 null,则继续。

    3. 如果 document 不被允许使用 permission,则返回 一个以 "NotAllowedError" DOMException 拒绝的 promise。

  14. 令 p 为一个新的 promise。

  15. 并行运行以下步骤:

    1. 令 credentials 为从凭据存储中收集 Credential的结果,给定 origin、 options 和 sameOriginWithAncestors。

    2. 如果 credentials 是一个异常,则以 credentials 拒绝 p。

    3. 如果以下所有陈述均为真,则以 credentials[0] 解决 p,并 跳过剩余步骤:

      1. credentials 的大小为 1

      2. origin 不需要用户中介

      3. options 是可先验匹配的。

      4. options.mediation 不是 "required"。

      5. options.mediation 不是 "conditional"。

      这 可能是错误的模型。如果能支持这样一种站点将会很好:它希望 同时接受用户名/密码或 WebAuthn 风格的凭据,而对于使用前者且希望保持登录状态的用户, 不必强制他们使用选择器。

    4. 如果 options 的 mediation 是 "silent", 则以 null 解决 p,并跳过 剩余步骤。

    5. 令 result 为要求用户 选择一个 Credential的结果,给定 options 和 credentials。

    6. 如果 result 是一个接口对象:

      1. 将 result 设置为执行 result 的 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 的结果,给定 origin、options 和 sameOriginWithAncestors。

        如果这抛出了一个异常:

        1. 令 e 为抛出的异常。

        2. 在 global 的DOM 操作任务 源上排入一个任务 以运行以下子步骤:

          1. 以 e 拒绝 p。

        3. 终止这些子步骤。

    7. 断言:result 是 null,或者是一个 Credential。

    8. 如果 result 是一个 Credential, 则以 result 解决 p。

    9. 如果 result 是 null,且 options.mediation 不是 conditional, 则以 result 解决 p。

      注意:如果 options.mediation 是 conditional 并且发现了一个 null 凭据, 则不会解决 promise p。

  16. 响应 p:

    1. 对于 interfaces 中的每个 interface:

      1. 从 settings 的 活动凭据类型中移除 interface 的 [[type]]。

  17. 返回 p。

2.5.2. 从凭证存储收集 Credential

给定 源(origin) (origin)、CredentialRequestOptions (options)、以及一个布尔值,只有在调用上下文与祖先同源 时为 true(sameOriginWithAncestors), 用户代理可以 从凭据存储收集 Credential ,返回本地存储并匹配 options 过滤条件的一组 Credential 对象。如果本地没有这样的 Credential ,返回集合为空:

  1. 令 possible matches 为一个空集合。

  2. 遍历 options 的 相关凭据接口对象 interface:

    1. 令 r 为执行 interface 的 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 内部方法后的结果,参数为 origin、options 和 sameOriginWithAncestors。如抛出 异常,则重新抛出该异常。

    2. 断言:r 为 接口对象列表。

    3. 遍历 r 中的每一项 c:

      1. 将 c 添加到 possible matches。

  3. 返回 possible matches。

2.5.3. 存储 Credential

存储 Credential 算法接收一个 Credential (credential),返回一个 Promise, 当对象持久化到凭证存储后 resolve。

  1. 令 settings 为当前设置对象。

  2. 断言:settings 是一个安全上下文。

  3. 如果 settings 的相关全局对象的关联 Document不是完全活动的, 则返回一个以 "InvalidStateError" DOMException 拒绝的 promise。

  4. 如果 credential 是源绑定的,并且 settings 的源是一个不透明源,则返回一个以 "SecurityError" DOMException 拒绝的 promise。

  5. 如果当前设置对象与其祖先同源,则令 sameOriginWithAncestors 为 true,否则为 false。

  6. 令 p 为一个新的 promise。

  7. 如果 settings 的活动凭据类型包含 credential 的 [[type]], 则返回一个以 "NotAllowedError" DOMException 拒绝的 promise。

  8. 将 credential 的 [[type]] 追加到 settings 的 活动凭据类型中。

  9. 并行运行以下步骤:

    1. 在 credential 和 sameOriginWithAncestors 上执行 credential 的接口对象的 [[Store]](credential, sameOriginWithAncestors) 内部方法。

      如果这抛出了一个异常:

      1. 令 e 为抛出的异常。

      2. 在 global 的DOM 操作任务源上排入一个任务,以 运行以下子步骤:

        1. 以 e 拒绝 p。

      否则,以 undefined 解决 p。

  10. 响应 p:

    1. 从 settings 的 活动凭据类型中移除 credential 的 [[type]]。

  11. 返回 p。

2.5.4. 创建 Credential

创建 Credential 算法接受一个 CredentialCreationOptions (options),并返回一个 Promise, 如果可以使用所提供的选项创建,则该 Promise 会兑现为一个 Credential ,如果无法创建任何 null,则为 Credential 。在异常 情况下,该 Promise 可能会以适当的异常拒绝:

  1. 令 settings 为当前设置对象。

  2. 断言:settings 是一个安全上下文。

  3. 令 global 为 settings 的全局对象。

  4. 令 document 为相关全局对象的关联 Document。

  5. 如果 document 不是完全活动的,则返回 一个以 "InvalidStateError" DOMException 拒绝的 promise。

  6. 如果当前设置对象与其祖先同源,则令 sameOriginWithAncestors 为 true,否则为 false。

  7. 令 interfaces 为由 options 的相关 凭据接口对象组成的集合。

  8. 如果以下任一陈述 为真,则返回一个以 NotSupportedError 拒绝的 promise:

    1. global 没有关联 Document。

    2. interfaces 的大小 大于 1。

      注意:将来的某个时候,放宽此限制或许是合理的,并 允许用户代理帮助用户从多种潜在凭据 类型中选择一种,以支持“注册”用例。不过目前,我们通过 将该字典限制为单个条目来暂不处理这一问题。

  9. 对于 interfaces 中的每个 interface:

    1. 令 permission 为 interface 的 [[type]] 的创建权限 策略。

    2. 如果 permission 为 null,则继续。

    3. 如果 document 不被允许使用 permission,则返回 一个以 "NotAllowedError" DOMException 拒绝的 promise。

  10. 如果 options.signal 已中止, 则返回一个以 options.signal 的 中止原因拒绝的 promise。

  11. 令 origin 为 settings 的源。

  12. 对于 interfaces 中的每个 interface:

    1. 如果 interface 的实例是源 绑定的,并且 origin 是 一个不透明源,则返回一个以 "SecurityError" DOMException 拒绝的 promise。

  13. 令 type 为 interfaces[0] 的 [[type]]。

  14. 如果 settings 的活动凭据类型包含 type,则返回 一个以 "NotAllowedError" DOMException 拒绝的 promise。

  15. 将 type 追加到 settings 的活动凭据类型中。

  16. 令 p 为一个新的 promise。

  17. 并行运行以下步骤:

    1. 令 r 为在 origin、options 和 sameOriginWithAncestors 上执行 interfaces[0] 的 [[Create]](origin, options, sameOriginWithAncestors) 内部方法的结果。

      如果这抛出了一个异常:

      1. 令 e 为抛出的异常。

      2. 在 global 的DOM 操作任务源上排入一个任务,以运行 以下子步骤:

        1. 以 e 拒绝 p。

      3. 终止这些子步骤。

    2. 如果 r 是一个 Credential 或 null,则以 r 解决 p,并终止这些子步骤。

    3. 断言:r 是一个算法(定义见§ 2.2.1.4 [[Create]] 内部方法)。

    4. 在 global 的DOM 操作任务源上排入一个任务,以运行 以下子步骤:

      1. 以给定 global 对 r 进行promise 调用所得的结果解决 p。

  18. 响应 p:

    1. 从 settings 的活动凭据类型中移除 type。

  19. 返回 p。

2.5.5. 防止静默访问

禁止静默访问算法 接收一个环境设置对象(settings), 返回一个 Promise :一旦 prevent silent access 标志被持久化到 凭据存储 后即 resolve。

  1. 令 origin 为 settings 的源。

  2. 如果 settings 的相关全局对象的关联 Document不是完全活动的, 则返回一个以 "InvalidStateError" DOMException 拒绝的 promise。

  3. 令 p 为一个新的 promise。

  4. 并行运行以下步骤:

    1. 在凭据存储中设置 origin 的阻止静默访问标志。

    2. 以 undefined 解决 p。

  5. 返回 p。

3. 密码凭证

无论好坏,许多网站都依赖用户名/密码对作为认证机制。 PasswordCredential 接口是一种凭证,用于实现该场景,既可以存储用户名和密码,也可以存储有助于用户在凭证选择器中选中正确账号的元数据。

3.1. 示例

3.1.1. 基于密码的登录

MegaCorp公司支持密码,并可通过 navigator.credentials.get() 从用户的凭证存储中获取 用户名/密码对:
navigator.credentials
  .get({ 'password': true })
  .then(credential => {
    if (!credential) {
      // 用户要么没有此站点的凭证,要么
      // 拒绝共享。此处可以插入代码降级为
      // 普通登录表单。
      return;
    }
    if (credential.type == 'password') {
      var form = new FormData();
      form.append('username_field', credential.id);
      form.append('password_field', credential.password);
      var opt = {
        method: 'POST',
        body: form,
        credentials: 'include'  // 携带 cookie。
      };
      fetch('https://example.com/loginEndpoint', opt)
        .then(function (response) {
          if (/* |response| 表示登录成功 */) {
            // 记录凭证已生效。见下方注释。
            navigator.credentials.store(credential);
            // 通知用户登录成功!可以进行登录后的操作!
            // 可以通过 location.href = '/signed-in-experience' 跳转到新页面
          } else {
            // 此处可以插入代码降级为基础登录表单。
          }
        });
    }
  });

另外,网站也可以直接将凭证数据写入 form 并调用 submit() 进行提交:

navigator.credentials
  .get({ 'password': true })
  .then(credential => {
    if (!credential) {
      return; // 同上...
    }
    if (credential.type === 'password') {
      document.querySelector('input[name=username_field]').value =
        credential.id;
      document.querySelector('input[name=password_field]').value =
        credential.password;
      document.getElementById('myform').submit();
    }
  });

需要注意,前一种方法更加推荐,因为它显式调用了 store() 并保存凭证。 基于form的机制 则依赖于表单提交,这会导致浏览上下文跳转,不易保证只有在登录成功后调用 store()。

注意: 用户代理展示的 凭证选择器 可能允许用户选择实际并未存储在当前源下的凭证。 例如,在登录https://www.example.com时,可能会提供来自https://m.example.com的凭证 (见§ 6.1 跨域凭证访问), 或允许用户临时新建一个凭证。 开发者可以通过在每次凭证被成功使用时都调用 store() (即便刚通过 get() 获取)来优雅地处理这种不确定性:如果凭证尚未存储在该源下,用户将有机会保存, 若已存储,则不会再提示。

3.1.2. 登录后确认

为了确保用户在成功登录后能够保存新凭证,可以将凭证传递给 store()。

如果用户通过 fetch() 将凭据提交到登录端点来登录,我们可以检查响应以确定 用户是否 已成功登录,并相应地通知用户代理。给定如下登录表单:
<form action="https://example.com/login" method="POST" id="theForm">
  <label for="username">用户名</label>
  <input type="text" id="username" name="username" autocomplete="username">
  <label for="password">密码</label>
  <input type="password" id="password" name="password" autocomplete="current-password">
  <input type="submit">
</form>

然后开发者可以使用类似以下的处理程序来处理表单提交:

document.querySelector('#theForm').addEventListener('submit', e => {
    if (window.PasswordCredential) {
      e.preventDefault();

      // 从触发 "submit" 事件的 HTMLFormElement 构造一个新的 PasswordCredential:
      // 这会获取标记有 "username" 和 "current-password" autocomplete
      // 属性的字段值:
      var c = new PasswordCredential(e.target);

      // 获取表单的 action URL,并将该新凭据对象作为
      // FormData 对象传入。如果响应表示成功,则告知用户代理,
      // 以便它可以询问用户是否存储密码供将来使用:
      var opt = {
        method: 'POST',
        body: new FormData(e.target),
        credentials: 'include'  // 发送 Cookie。
      };
      fetch(e.target.action, opt).then(r => {
        if (/* |r| 是一个“成功的” Response */)
          navigator.credentials.store(c);
      });
    }
});

3.1.3. 修改密码

同样的存储机制可用于“修改密码”,无需任何修改:当用户更改凭证时,网站可通知用户代理用户已用新凭证成功登录。用户代理随后可以更新其存储的凭证:

MegaCorp Inc. 允许用户通过异步向 后端服务器 POST 数据来更改密码。成功完成后,他们可以 通过使用新信息调用 store() 来更新用户的凭据。

给定如下密码更改表单:

<form action="https://example.com/changePassword" method="POST" id="theForm">
  <input type="hidden" name="username" autocomplete="username" value="user">
  <label for="password">新密码</label>
  <input type="password" id="password" name="password" autocomplete="new-password">
  <input type="submit">
</form>

开发者可以使用类似以下的处理程序来处理表单提交:

document.querySelector('#theForm').addEventListener('submit', e => {
  if (window.PasswordCredential) {
    e.preventDefault();

    // 从触发 "submit" 事件的 HTMLFormElement 构造一个新的 PasswordCredential
    // 这会获取标记有 "username" 和 "new-password" autocomplete
    // 属性的字段值:
    var c = new PasswordCredential(e.target);

    // 获取表单的 action URL,并将该新凭据对象作为
    // FormData 对象传入。如果响应表示成功,则告知用户代理,
    // 以便它可以询问用户是否存储密码供将来使用:
    var opt = {
      method: 'POST',
      body: new FormData(e.target),
      credentials: 'include'  // 发送 Cookie。
    };
    fetch(e.target.action, opt).then(r => {
      if (/* |r| 是一个“成功的” Response */)
        navigator.credentials.store(c);
    });
  }
});

3.2. PasswordCredential 接口

[Exposed=Window,
 SecureContext]
interface PasswordCredential : Credential {
  constructor(HTMLFormElement form);
  constructor(PasswordCredentialData data);
  readonly attribute USVString password;
};
PasswordCredential includes CredentialUserData;

partial dictionary CredentialRequestOptions {
  boolean password = false;
};
password, 类型为 USVString,只读

此属性表示凭据的密码。

[[type]]

PasswordCredential 接口对象有一个名为 [[type]] 的内部槽,其 值为 "password"。

[[discovery]]

PasswordCredential 接口对象有一个名为 [[discovery]] 的内部槽,其值为 "凭据存储"。

PasswordCredential(form)

此构造函数接受一个 HTMLFormElement (form),并运行以下步骤:

  1. 令 origin 为当前设置对象的源。

  2. 令 r 为给定 form 和 origin 执行从 HTMLFormElement 创建 PasswordCredential的结果。

  3. 如果 r 是一个异常,则抛出 r。

    否则,返回 r。

PasswordCredential(data)

此构造函数接受一个 PasswordCredentialData (data),并运行以下步骤:

  1. 令 r 为在 data 上执行从 PasswordCredentialData 创建 PasswordCredential的结果。

  2. 如果 r 是一个异常,则抛出 r。

    否则,返回 r。

PasswordCredential 对象可以通过 navigator.credentials.create() 创建,既可以通过显式传入一个 PasswordCredentialData 字典,也可以基于 一个 HTMLFormElement 的 可提交元素的内容。

dictionary PasswordCredentialData : CredentialData {
  USVString name;
  USVString iconURL;
  required USVString origin;
  required USVString password;
};

typedef (PasswordCredentialData or HTMLFormElement) PasswordCredentialInit;

partial dictionary CredentialCreationOptions {
  PasswordCredentialInit password;
};

PasswordCredential 对象是源绑定的。

PasswordCredential 的 接口对象继承 Credential 的 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 实现,并定义 自己的 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)、 [[Create]](origin, options, sameOriginWithAncestors) 和 [[Store]](credential, sameOriginWithAncestors) 的实现。

3.3. 算法

3.3.1. PasswordCredential 的 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)

[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 使用一个源(origin)、一个 CredentialRequestOptions (options), 以及一个布尔值调用,当且仅当调用上下文与其祖先 同源时,该布尔值为 true (sameOriginWithAncestors)。 该算法从 凭据存储中返回一组 Credential 对象。如果没有可用的匹配 Credential 对象,则返回的集合 将为空。

如果 sameOriginWithAncestors 不是 true,该算法将抛出 NotAllowedError。

  1. 断言:options["password"] 存在。

  2. 如果 sameOriginWithAncestors 为 false,则抛出一个 "NotAllowedError" DOMException。

    注意:此限制旨在解决 § 6.4 源混淆中提出的问题。

  3. 如果 options["password"] 不是 true,则返回空集合。

  4. 返回从凭据存储中检索 与以下 过滤器匹配的凭据的结果:

    1. 该凭据是一个 PasswordCredential

    2. 该凭据的 [[origin]] 与 origin 同源。

3.3.2. PasswordCredential 的 [[Create]](origin, options, sameOriginWithAncestors)

[[Create]](origin, options, sameOriginWithAncestors) 使用一个源(origin)、一个 CredentialCreationOptions (options),以及一个布尔值调用,当且仅当调用 上下文与其祖先同源时,该布尔值为 true (sameOriginWithAncestors)。 如果能够创建一个 PasswordCredential, 则该算法返回它, 否则返回 null。CredentialCreationOptions 字典必须有一个 password 成员,该成员 保存一个 HTMLFormElement 或一个 PasswordCredentialData。 如果该成员的值无法 用于创建一个 PasswordCredential, 则此算法将抛出一个 TypeError 异常。

  1. 断言:options["password"] 存在,并且 sameOriginWithAncestors 未用到。

  2. 如果 options["password"] 是 HTMLFormElement, 返回执行“从 HTMLFormElement 创建 PasswordCredential” 且以 options["password"] 和 origin 作为参数的结果。 捕获并重新抛出任何异常。

  3. 如果 options["password"] 是 PasswordCredentialData, 返回 执行“从 PasswordCredentialData 创建 PasswordCredential” 并以 options["password"] 作为参数的结果。 捕获并重新抛出任何异常。

  4. 抛出 TypeError 异常。

3.3.3. PasswordCredential 的 [[Store]](credential, sameOriginWithAncestors)

[[Store]](credential, sameOriginWithAncestors) 使用一个 PasswordCredential (credential)以及一个布尔值调用,当且仅当调用 上下文与其祖先同源时,该布尔值为 true (sameOriginWithAncestors)。一旦 credential 被持久化到凭据存储中,该算法就返回 undefined。

如果 sameOriginWithAncestors 不是 true,该算法将返回一个 NotAllowedError。

  1. 如果 sameOriginWithAncestors 为 false,则在不更改用户代理的 凭据存储的情况下抛出一个 "NotAllowedError" DOMException。

    注意:此限制旨在解决 § 6.4 源混淆中提出的问题。

  2. 如果用户代理的凭据存储包含一个 PasswordCredential (stored), 其 id 属性是 credential 的 id 并且其 [[origin]] 槽与 credential 的 [[origin]] 同源, 则:

    1. 如果用户授予更新凭据的权限(如定义 用户 中介时所讨论的),则:

      1. 将 stored 的 password 设置为 credential 的 password。

      2. 将 stored 的 name 设置为 credential 的 name。

      3. 将 stored 的 iconURL 设置为 credential 的 iconURL。

    否则,如果用户授予存储凭据的权限(如定义 用户 中介时所讨论的,则:

    1. 在凭据存储中存储一个具有以下 属性的 PasswordCredential:

      id

      credential 的 id

      name,

      credential 的 name

      iconURL

      credential 的 iconURL

      [[origin]]

      credential 的 [[origin]]

      password

      credential 的 password

3.3.4. 通过 HTMLFormElement 创建 PasswordCredential

要在给定一个 HTMLFormElement (form)和一个源(origin)的情况下,从 HTMLFormElement 创建一个 PasswordCredential,运行以下步骤。

注意:§ 3.1.2 登录后确认和§ 3.1.3 更改密码 提供了预期 用法的示例。

  1. 令 data 为一个新的 PasswordCredentialData 字典。

  2. 将 data 的 origin 成员的值设置为 origin 的值。

  3. 令 formData 为在 form 上执行 FormData 构造函数 的结果。

  4. 令 elements 为所有可提交元素的列表,这些元素的表单所有者是 form,并按树 顺序排列。

  5. 令 newPasswordObserved 为 false。

  6. 对于 elements 中的每个 field,运行以下步骤:

    1. 如果 field 没有 autocomplete 属性,则跳到下一个 field。

    2. 令 name 为 field 的 name 属性的值。

    3. 如果在 name 上执行 formData 的 has() 方法返回 false,则 跳到下一个 field。

    4. 如果 field 的 autocomplete 属性的值包含一个或多个自动填充详细信息令牌(tokens), 则:

      1. 对于 tokens 中的每个 token:

        1. 如果 token 与以下字符串之一进行ASCII 不区分大小写匹配, 则运行 相应步骤:

          "new-password"

          将 data 的 password 成员的 值设置为在 name 上执行 formData 的 get() 方法的结果,并将 newPasswordObserved 设置为 true。

          "current-password"

          如果 newPasswordObserved 为 false, 则将 data 的 password 成员的 值设置为在 name 上执行 formData 的 get() 方法的结果。

          注意:通过 检查 newPasswordObserved 是否为 false, new-password 字段优先于 current-password 字段。

          "photo"

          将 data 的 iconURL 成员的 值设置为在 name 上执行 formData 的 get() 方法的结果。

          "name"
          "nickname"

          将 data 的 name 成员的 值设置为在 name 上执行 formData 的 get() 方法的结果。

          "username"

          将 data 的 id 成员的值设置为 在 name 上执行 formData 的 get() 方法的 结果。

  7. 令 c 为在 data 上执行从 PasswordCredentialData 创建一个 PasswordCredential的结果。如果这抛出了一个异常,则重新抛出该 异常。

  8. 断言:c 是一个 PasswordCredential。

  9. 返回 c。

3.3.5. 通过 PasswordCredentialData 创建 PasswordCredential

要利用 PasswordCredentialData 创建 PasswordCredential,给定一个 PasswordCredentialData (data),按如下步骤操作。

  1. 令 c 为一个新的 PasswordCredential 对象。

  2. 如果以下任一项为空字符串,则抛出 TypeError 异常:

    • data 的 id 成员的值

    • data 的 origin 成员的值

    • data 的 password 成员的值

  3. 设置 c 的属性如下:

    password

    data 的 password 成员的值

    id

    data 的 id 成员的值

    iconURL

    data 的 iconURL 成员的值

    name

    data 的 name 成员的值

    [[origin]]

    data 的 origin 成员的值。

  4. 返回 c。

3.3.6. CredentialRequestOptions 与 PasswordCredential 的匹配

给定一个 CredentialRequestOptions (options),如果 PasswordCredential 应可作为 get() 请求的响应,则以下算法返回 "Matches",否则返回 "Does Not Match"。

  1. 如果 options 有一个 password 成员,其值为 true,则 返回 "Matches"。

  2. 返回 "Does Not Match"。

4. 联合凭证

4.1. FederatedCredential 接口

[Exposed=Window,
 SecureContext]
interface FederatedCredential : Credential {
  constructor(FederatedCredentialInit data);
  readonly attribute USVString provider;
  readonly attribute DOMString? protocol;
};
FederatedCredential includes CredentialUserData;

dictionary FederatedCredentialRequestOptions {
  sequence<USVString> providers;
  sequence<DOMString> protocols;
};

partial dictionary CredentialRequestOptions {
  FederatedCredentialRequestOptions federated;
};
provider, 类型为 USVString,只读

凭据的联合身份提供者。关于有效格式详见 § 4.1.1 标识 Provider。

protocol, 类型为 DOMString,只读,允许为 null

凭据的联合身份提供者协议(例如 "openidconnect")。如果该值为 null, 可根据 provider 推断协议。

[[type]]

FederatedCredential 接口对象具有名为 [[type]] 的内部槽, 其值为 "federated"。

[[discovery]]

FederatedCredential 接口对象具有名为 [[discovery]] 的内部槽, 其值为 "credential store"。

FederatedCredential(data)

该构造函数接受 FederatedCredentialInit (data),并执行下列步骤:

  1. 令 r 为执行 “从 FederatedCredentialInit 创建 FederatedCredential” 的结果。如果抛出 异常,则重新抛出。

  2. 返回 r。

FederatedCredential 对象可以通过将 FederatedCredentialInit 字典 传递给 navigator.credentials.create() 来创建。

dictionary FederatedCredentialInit : CredentialData {
  USVString name;
  USVString iconURL;
  required USVString origin;
  required USVString provider;
  DOMString protocol;
};

partial dictionary CredentialCreationOptions {
  FederatedCredentialInit federated;
};

FederatedCredential 对象是与源绑定的。

FederatedCredential 的 接口对象继承自 Credential 的 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 实现, 并自行定义 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)、 [[Create]](origin, options, sameOriginWithAncestors) 以及 [[Store]](credential, sameOriginWithAncestors) 的实现。

注意: 如果将来用户代理支持代表用户获取认证令牌,可通过实现 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 来实现此能力。

4.1.1. 身份提供者标识

每个网站在引用某个特定联合身份提供者时应使用相同的标识符。例如,Facebook Login 不应被称为 "Facebook"、"Facebook Login"、"FB"、"FBL"、"Facebook.com" 等等。应有一个规范标识符供所有人使用,因为一致的标识有助于用户代理提供帮助。

为保持一致,传递到本文档定义的 API(如 FederatedCredentialRequestOptions 的 providers 数组或 FederatedCredential 的 provider 属性)必须用该提供者用于登录的 origin 的 ASCII 序列化来标识。也就是说,Facebook 用 https://www.facebook.com,Google 用 https://accounts.google.com。

这种 origin 的序列化不包括结尾的 U+002F 斜杠("/"),但用户代理应默默接受它:https://accounts.google.com/ 显然与 https://accounts.google.com 相同。

4.2. 算法

4.2.1. FederatedCredential 的 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)

[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 被调用时,参数为 origin(origin),CredentialRequestOptions (options), 以及仅当调用上下文与祖先同源 时为 true 的布尔值(sameOriginWithAncestors)。 该算法返回来自凭据存储的一组 Credential 对象。如果无匹配的 Credential ,则返回集合为空。

  1. 断言:options["federated"] 存在。

  2. 如果 sameOriginWithAncestors 为 false,抛出 "NotAllowedError" DOMException。

    注意: 此限制旨在解决 § 6.4 源混淆 中提出的问题。

  3. 如果 options["federated"] 不是 true,则返回空集合。

  4. 返回检索 来自凭据存储且符合下列条件的凭据的结果:

    1. 该凭据是 FederatedCredential

    2. 该凭据的 [[origin]] 与 origin 同源。

    3. 如果 options["federated"]["providers"] 存在,其值 包含该凭据的 provider。

    4. 如果 options["federated"]["protocols"] 存在,其值 包含该凭据的 protocol。

4.2.2. FederatedCredential 的 [[Create]](origin, options, sameOriginWithAncestors)

[[Create]](origin, options, sameOriginWithAncestors) 被调用时,参数为 origin(origin)、CredentialCreationOptions (options),以及一个仅当调用上下文与其祖先同源时为 true 的布尔值(sameOriginWithAncestors)。 该算法在可以创建时返回一个 FederatedCredential,否则返回 null,异常情况下抛出 异常:

  1. 断言:options["federated"] 存在,且 sameOriginWithAncestors 未使用。

  2. 设置 options["federated"] 的 origin 成员值为 origin 的值。

  3. 返回执行 通过 FederatedCredentialInit 创建 FederatedCredential, 参数为 options["federated"] 的结果。如果抛出 异常,则重新抛出该异常。

4.2.3. FederatedCredential 的 [[Store]](credential, sameOriginWithAncestors)

[[Store]](credential, sameOriginWithAncestors) 被调用时,传入一个 FederatedCredential (credential),以及一个仅当调用上下文与其祖先同源时为 true 的布尔值 (sameOriginWithAncestors)。当 credential 持久化到 凭证存储后,该算法返回 undefined。

如果 sameOriginWithAncestors 不是 true,算法将返回 NotAllowedError。

  1. 如果 sameOriginWithAncestors 为 false,则抛出 "NotAllowedError" DOMException, 并且不修改用户代理的 凭证存储。

    注意:此限制旨在解决 § 6.4 源混淆中提出的问题。

  2. 如果用户代理的 凭证存储中已包含一个 FederatedCredential, 其 id 属性等于 credential 的 id, 且其 [[origin]] 槽与 credential 的 [[origin]]同源, 且其 provider 等于 credential 的 provider, 则直接返回。

  3. 如果用户允许存储凭证(见 用户介入 的定义),则在 凭证存储中存储一个 FederatedCredential, 其属性如下:

    id

    credential 的 id

    name

    credential 的 name

    iconURL

    credential 的 iconURL

    [[origin]]

    credential 的 [[origin]]

    provider

    credential 的 provider

    protocol

    credential 的 protocol

4.2.4. 通过 FederatedCredentialInit 创建 FederatedCredential

要 通过 FederatedCredentialInit 创建 FederatedCredential,给定一个 FederatedCredentialInit (init),运行如下步骤。

  1. 令 c 为一个新的 FederatedCredential 对象。

  2. 如果以下任意项是空字符串,则抛出 TypeError 异常:

  3. 设置 c 的属性如下:

    id

    init.id 的值

    provider

    init.provider 的值

    iconURL

    init.iconURL 的值

    name

    init.name 的值

    [[origin]]

    init.origin 的值。

  4. 返回 c。

5. 用户介入

通过 API 向 Web 暴露凭据信息可能会对用户隐私产生多种影响。因此,用户代理**必须**在多种场景下让用户参与,以确保用户明白发生了什么、他们的凭据将与谁共享。

我们称某个操作为用户调解,如果它是在获得用户明确同意后发生的。例如,同意可能通过用户在 凭据选择器界面上的直接交互来表达。通常,用户调解操作会涉及向用户展示某种界面,并请其做出决策。

未调解的(unmediated)操作则是在没有用户明确同意的情况下静默发生。例如,如果用户设置浏览器给予某个源持久的凭据访问权限,则可在不弹出决策界面的情况下提供凭据。

下面我们将阐述适用于所有凭据类型的一些通用要求,但请注意,用户代理具有较大自由度(它处于帮助用户的特权位置)。此外,特定凭据类型可能还有超出本文一般性要求的特殊规定。

5.1. 存储和更新凭证

凭据信息属于敏感数据,用户**必须**始终掌控这类信息的存储。无意间的凭据存储,例如,可能会让用户在某台设备上的本地资料与某个特定网络身份意外关联起来。为了降低此类意外风险:

  1. 未经用户调解,不应存储或更新凭据信息。 例如,用户代理可以在每次调用 store() 时,弹出“是否保存此凭据?”对话框。

    如果用户代理希望提供持久授权(如“始终保存密码”选项),可推断用户已同意(不过我们建议用户代理把范围尽量收窄——比如“始终保存_生成的_密码”或“始终保存本站密码”)。

  2. 用户代理在存储凭据时应该通知用户,例如通过在地址栏或类似位置显示图标等形式。

  3. 用户代理**必须**允许用户手动移除已存储的凭据。该功能可作为设置页面提供,或通过如上所述的通知界面实现。

5.2. 强制用户介入

默认情况下,所有用户调解对 所有源都是必需的,因为 防止静默访问标志 在凭据存储中被设置为 true。用户可以选择为某个源授予凭据的持续访问权 (例如“保持登录本站”选项),此时会将该标志设为 false。这样,用户总会自动登录该站点, 从易用性和便捷性角度来看很理想,但也可能带来意外影响(例如,有的用户代理会在多设备间同步这个标志状态)。

为了避免这种意外风险:

  1. 用户代理必须允许用户为某一源或所有源要求用户调解。该功能可作为全局开关实现,覆盖每个源的 防止静默访问标志,让其都返回 false, 也可以为指定源(或特定源的具体凭据)做更细粒度的设置。

  2. 用户代理不得在没有用户调解的情况下,将某个源的防止静默访问标志 设为 false。例如,凭据选择器 (见§ 5.3 凭据选择)可带有一个复选框,允许用户勾选后为该源在无调解时开放凭据, 或用户代理可在其凭据管理器引导流程中让用户设置默认选项。

  3. 用户代理在为某个源提供凭据时必须通知用户,比如可以在地址栏或类似位置显示图标。

  4. 若用户清除某个源的浏览数据(如 cookies、localStorage 等),用户代理必须将该源的 防止静默访问标志 设为 true。

5.3. 凭证选择

当对某个需要 用户调解的来源调用 get() 时,用户代理**必须**征求用户同意后方可共享凭据信息。 这通常应以凭据选择器的形式呈现:向用户展示可用于该站点的凭据列表, 让他们选择要提供给网站的凭据,或者完全取消此次请求。

选择器 的用户界面应以某种方式易于与网站自身产生的界面区分。例如,选择器可以以用户代理界面重叠且不可伪造的方式显示。

选择器 的界面**必须**包含请求凭据的来源信息。

选择器 的界面应包含关联于请求凭据来源的所有 Credential 对象。

用户代理可以为每个 Credential 对象内部关联额外信息(超出本文档规定属性), 以提升选择器的实用性。例如,可以通过 Favicons 区分不同身份提供方等。但所有这种附加信息**不得**直接暴露给 Web。

选择器 的交互细节未在此规定:鼓励用户代理探索不同 UI 设计,以帮助用户理解认证选项,并引导用户流程做出凭据选择。 不过选择器的接口如下:

用户代理可以让用户选择 Credential,参数包括 CredentialRequestOptions (options) 及从 凭据存储中发现的一组 Credential 对象(locally discovered credentials)。

此算法返回值可以是 null(用户拒绝与站点共享凭据时), 也可以是用户所选的 Credential 对象, 或者如果用户选择了某类型凭据,则为相关 Credential 接口对象。

选择器界面可以将 locally discovered credentials 列表展示给用户,可能类似如下高度非规范的界面草图:

A mockup of what a chooser might look like.

如果 options 不 先验可匹配, 选择器可能还应列出 options 中未在已有凭据列表里的相关凭据接口对象。例如,某站点支持 webauthn 风格认证器,则“安全密钥”可带图标列入选择器选项。

另请注意,在某些情况下用户代理可能会跳过显示选择器。 例如,如果唯一的相关凭据接口对象本身就需要用户操作, 用户代理可直接返回该接口,并依赖其内部调解流程完成用户授权。

6. 安全注意事项

以下各节为各种安全和隐私注意事项的指导原则。不同凭证类型可能会强制执行更严格或更宽松的版本。

6.1. 跨域凭证访问

凭据属于敏感信息,用户代理在判断何时可以安全地与网站共享时必须格外谨慎。最安全的做法是仅允许凭据分享到其被保存时的确切来源(origin)。然而,这对于 Web 来说往往过于严格:比如功能被分在 example.com 和 admin.example.com 等子域名中的网站。

为在减少用户困扰和保障凭据安全之间取得平衡,用户代理:

  1. 绝不能在 scheme 组件代表安全性降级的两个源之间共享凭证。也就是说, 可以考虑允许保存在 http://example.com/ 上的凭证对 https://example.com/ 可用 (鼓励开发者迁移到安全传输),但反过来则很危险。

  2. 可以使用公共后缀列表 [PSL] 通过比较凭证 [[origin]] 的可注册域名 与调用 get() 时的 origin,来决定一个凭证的有效作用域。即: 保存在 https://admin.example.com/ 和 https://example.com/ 的凭证可以在 get() 从 https://www.example.com/ 调用时提供给用户,反过来也可以。

  3. 如果凭证的 origin 不是调用 origin 的精确匹配, 绝不能在没有用户介入 的情况下,响应 get() 调用而向一个 origin 提供凭证。也就是说, Credential 对象如果属于 https://example.com, 不会直接返回给 https://www.example.com, 只能通过选择器供用户选择。

6.2. 凭证泄露

开发者应当采取一些预防措施,通过设置合理的内容安全策略(Content Security Policy,[CSP]),限制数据可以发送到的端点,从而降低跨站脚本攻击演变为对用户账户的持久访问的风险。特别是,开发者应确保在页面策略中显式或隐式地设置以下指令:

当然,开发者还应正确转义输入输出,并考虑使用其他防护措施,比如 Subresource Integrity [SRI] 进一步降低风险。

定义具体凭证类型时,应充分考虑凭证数据的传输方式。例如,可以定义仅限同源端点的传输机制。

6.3. 不安全站点

用户代理不得在非安全环境中暴露此处定义的 API。用户代理可能实现自动填充机制,在非潜在可信URL上存储用户凭证并填充登录表单,但这些站点无法被信任以任何有意义的方式直接与凭证管理器交互,并且这些站点不得访问存储在安全环境中的凭证。

6.4. 源混淆

如果被嵌入的页面能够访问本规范定义的 API,可能会导致用户被误导,将凭证授权给非顶级浏览上下文的源,而用户只能合理理解顶级上下文的安全源。

本规范将凭证管理 API 暴露给这些上下文,因为某些凭证类型在用户代理给予足够 UI 支持和上下文提示后,可以安全地暴露。

但某些具体凭证类型很难在这些上下文安全暴露,因此通过在 [[Create]](origin, options, sameOriginWithAncestors)、 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)、 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 和 [[Store]](credential, sameOriginWithAncestors) 方法中进行检查来加以限制。

例如,PasswordCredential 的 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 方法如果在 Worker 或非 顶级浏览上下文中调用,会立即返回空集合。

6.5. 注销

如 § 5.2 强制用户介入 所述,如果用户选择自动登录网站,则用户代理会在网站请求时提供凭证。网站可通过调用 CredentialsContainer 的 preventSilentAccess() 方法,关闭某源的自动登录行为。

用户代理依赖网站正确操作;若网站疏忽或恶意,未调用此方法,用户代理仍会继续提供凭证,这比网站在用户点击“注销”时不清理凭证更糟,因为用户代理也参与了认证过程。

用户必须能够控制此行为。如 § 5.2 强制用户介入 所述,清除某源的 Cookie 也会将该源在 凭证存储中的防止静默访问标志重置为 true。此外,用户代理应为禁用某源自动登录提供 UI 控件,比如与凭证已被提供通知绑定的选项。

7. 隐私注意事项

7.1. 计时攻击

如果用户没有针对某个源的凭证,调用 get() 时会非常快速地返回结果。恶意网站可能据此区分“没有凭证”与“有凭证但用户选择不分享”的用户。

用户代理也应对凭证请求进行速率限制。短时间内多次请求凭证几乎肯定属于滥用行为。

7.2. 选择器泄露

如果用户代理的凭据选择器会展示来源提供的图片(例如 Credential 显示站点 favicon),那么对这些图片的请求**不得**直接与选择器的实例化关联,以避免泄露选择器的使用情况。一种做法是在保存或更新 Credential 时后台获取图片, 并在Credential生命周期内缓存。

这些图片**必须**使用如下 fetch 设置获取:credentials mode 设为 "omit", service-workers mode 设为 "none", client 设为 null, initiator 设为空字符串, destination 设为 "subresource"。

此外,如果用户代理允许用户更改凭据关联的名称或图标,则这种数据变动**不应**暴露给网站 (考虑某用户给同一来源的两个凭据命名为“我的小号”和“我的主号”等情况)。

7.3. 本地存储数据

该 API 允许某个 源将数据与用户的个人资料持久存储。由于大多数用户代理对凭证数据和“浏览数据”(如 cookie 等)处理方式不同,这可能导致用户在清除 cookie 时以为所有来源痕迹都被清除,实际仍有凭证数据保留而感到意外。

用户代理应提供 UI,清楚地向用户展示某个源已存储凭证数据,并应让用户易于移除这些数据。

8. 实现注意事项

本节为非规范内容。

8.1. 网站作者

此处应补充关于何时及如何使用该 API 的一些思考,尤其是关于 mediation 的相关内容。 [w3c/webappsec Issue #290]

说明通过 fetch() 携带 FormData body 提交凭据时的编码限制。

为某一凭据类型做特性检测时,建议开发者验证相关 Credential 的具体实现是否存在,而不要只依赖 navigator.credentials 是否存在。后者只能判断 API 是否提供,但不能确保给定站点需要的那种凭据类型已获支持。例如,若某站点需要密码型凭据,检测 if (window.PasswordCredential) 是最有效的支持性判断方式。

8.2. 扩展点

本文档提供了一个通用的、高层的API,设计之初就支持通过具体的凭据类型扩展,以满足不同认证场景的需求。实现过程希望应当很直观:

  1. 定义一个继承自 Credential 的新接口:

    [Exposed=Window,
     SecureContext]
    interface ExampleCredential : Credential {
      // Definition goes here.
    };
    
  2. 在 ExampleCredential 的 接口对象上定义合适的 [[Create]](origin, options, sameOriginWithAncestors)、 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)、 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 和 [[Store]](credential, sameOriginWithAncestors) 方法。[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 适用于永远 有效 并能直接从凭证存储 取出的凭证, 而 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 则适用于需从凭证源 重新生成的凭证。

    对于类似 PublicKeyCredential 的长时间操作, 包括 [[Create]](origin, options, sameOriginWithAncestors) 与 [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) ,推荐使用 options.signal 以允许开发者中止操作。详细说明见 DOM § 3.3 在 API 中使用 AbortController 和 AbortSignal 对象。

    ExampleCredential 的 [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 内部方法会被传入 origin(origin), CredentialRequestOptions 对象(options),以及一个仅调用上下文 与祖先同源时为 true 的布尔值。 算法返回与选项匹配的一组 Credential 对象。如没有匹配的 Credential ,则返回空集。
    1. 断言:options[example] 存在。

    2. 如果 options[example] 不为真,返回空集。

    3. 遍历凭证存储中的每个credential:

      1. ...

  3. 定义 ExampleCredential 接口对象 的 [[type]] 槽的值:

    ExampleCredential 接口对象有一个名为 [[type]] 的内部槽, 其值为字符串 "example"。
  4. 定义 ExampleCredential 接口对象 的 [[discovery]] 槽的值:

    ExampleCredential 接口对象有一个名为 [[discovery]] 的内部槽, 其值为 "credential store"。
  5. 扩展 CredentialRequestOptions ,加入新凭证类型需要的选项以便能合理响应 get():

    dictionary ExampleCredentialRequestOptions {
      // Definition goes here.
    };
    
    partial dictionary CredentialRequestOptions {
      ExampleCredentialRequestOptions example;
    };
    
  6. 扩展 CredentialCreationOptions ,加入新凭证类型用于响应 create() 时所需的创建数据:

    dictionary ExampleCredentialInit {
      // Definition goes here.
    };
    
    partial dictionary CredentialCreationOptions {
      ExampleCredentialInit example;
    };
    
  7. 若新凭证类型支持 conditional 用户介入, 则定义 ExampleCredential/isConditionalMediationAvailable() 以 返回 resolve 为 true 的 promise。

  8. 依照§ 2.1.2.1 注册表项要求与更新流程,为新的 "example" 凭证类型及其内容 在 凭证类型注册表中添加一个条目,包括:

    注: 凭据类型的选项 字典的 选项成员 标识符在 CredentialCreationOptions 和 CredentialRequestOptions 中必须相同, 并且应当 与 Credential 接口对象的 [[type]] 槽中的 凭据类型值相同。

你可能还会发现有必要增加新原语。例如在某些复杂的多因子登录流程中需要一次性返回多个 Credential 对象。可以以通用方式为 CredentialsContainer 增设 getAll() 方法,返回一个 sequence<Credential>,并定义合理的机制来支持请求不同类型的凭据。

如有此类扩展,建议联系 public-webappsec@ 咨询和评审。

8.3. 浏览器扩展

理想情况下,支持扩展系统的用户代理会允许第三方钩入这些 API 端点,以改善第三方凭证管理软件的行为,就像用户代理通过命令式方式改善自身一样。

这既可以是用户代理中介的复杂新 API,也可以仅允许扩展覆盖 get() 和 store() 端点以满足自身需求。

9. 未来工作

本节为非规范内容。

此处定义的 API 仅以极简方式将用户代理的凭证管理器暴露给 Web,并允许 Web 协助这些凭证管理器识别何时正在使用联合身份提供者。下一步的合理方向,将会类似于[WEB-LOGIN]等文档中描绘的内容(以及在某种程度上,Mozilla 的 BrowserID [BROWSERID])。

用户代理处于一个独特位置,可以有效调解用户、身份提供者和网站之间的关系。如果用户代理能减少典型认证流程中的风险和困惑,用户的处境将比现在大为改善。

一种自然的信息暴露方式可能是扩展 FederatedCredential 接口,增加如认证令牌等属性,或添加某种声明提供者支持认证类型的 manifest 格式属性。

此处描述的 API 设计为足够可扩展,以支持需要用户交互的用例,甚至可能涉及除了请求凭证的网站之外的其他网站。我们希望我们采用的基于 Promise 的系统,足够扩展以支持此类异步流程,这些流程可能需要多个浏览上下文之间的某种交互(例如 idp.com 上的调解活动可解析传回给 rp.com 的 Promise),或者未来支持设备与用户代理之间的交互(例如 [WEBAUTHN]),而无需从零重新设计 API。

循序渐进。

索引

本规范定义的术语

引用文献定义的术语

参考文献

规范性引用

[CSP]
Mike West; Antonio Sartori. 内容安全策略第 3 级. 2026 年 8 月 13 日. 工作草案. URL:https://www.w3.org/TR/CSP3/
[DIGITAL-CREDENTIALS]
Marcos Cáceres; Sam Goto. 数字 凭证. URL:https://wicg.github.io/digital-credentials/
[DOM]
Anne van Kesteren. DOM 标准. 现行标准. URL:https://dom.spec.whatwg.org/
[FEDCM]
Nicolas Pena Moreno. 联合凭证管理 API. 2024 年 8 月 20 日. 首次公开工作草案. URL:https://www.w3.org/TR/fedcm-1/
[FETCH]
Anne van Kesteren. Fetch 标准. 现行 标准. URL:https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; 等. HTML 标准. 现行标准. URL:https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL:https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. 权限. 2025 年 10 月 6 日. 工作草案. URL:https://www.w3.org/TR/permissions/
[PROMISES-GUIDE]
Domenic Denicola. 编写使用 Promise 的 规范. 2018 年 11 月 9 日. TAG 结论. URL:https://www.w3.org/2001/tag/doc/promises-guide
[PSL]
公共后缀列表. URL:https://publicsuffix.org/
[SECURE-CONTEXTS]
Mike West. 安全上下文. 2023 年 11 月 10 日. 候选推荐草案. URL:https://www.w3.org/TR/secure-contexts/
[WEB-OTP]
WebOTP API. 社区组报告草案. URL:https://wicg.github.io/web-otp/
[WEBAUTHN-3]
Akshay Kumar; 等. Web 认证:用于 访问公钥凭证的 API - 第 3 级. 2026 年 5 月 26 日. 候选推荐标准. URL:https://www.w3.org/TR/webauthn-3/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 现行 标准. URL:https://webidl.spec.whatwg.org/
[XHR]
Anne van Kesteren. XMLHttpRequest 标准. 现行 标准. URL:https://xhr.spec.whatwg.org/

资料性引用

[BROWSERID]
Ben Adida 等。BrowserID。2013年2月26日。URL: https://github.com/mozilla/id-specs/blob/prod/browserid/index.md
[SRI]
Frederik Braun。子资源完整性(Subresource Integrity)。2026年3月20日。WD。URL: https://www.w3.org/TR/sri-2/
[WEB-LOGIN]
Jason Denizac;Robin Berjon;Anne van Kesteren。web-login。URL: https://github.com/jden/web-login
[WEBAUTHN]
Dirk Balfanz 等。Web Authentication:公钥凭证访问API 第1级(Web Authentication:An API for accessing Public Key Credentials Level 1)。2019年3月4日。REC。URL: https://www.w3.org/TR/webauthn-1/
[XMLHTTPREQUEST]
Anne van Kesteren 等。XMLHttpRequest 第1级。2016年10月6日。NOTE。URL: https://www.w3.org/TR/XMLHttpRequest/

IDL 索引

[Exposed=Window, SecureContext]
interface Credential {
  readonly attribute USVString id;
  readonly attribute DOMString type;
  static Promise<boolean> isConditionalMediationAvailable();
};

[SecureContext]
interface mixin CredentialUserData {
  readonly attribute USVString name;
  readonly attribute USVString iconURL;
};

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute CredentialsContainer credentials;
};

[Exposed=Window, SecureContext]
interface CredentialsContainer {
  Promise<Credential?> get(optional CredentialRequestOptions options = {});
  Promise<undefined> store(Credential credential);
  Promise<Credential?> create(optional CredentialCreationOptions options = {});
  Promise<undefined> preventSilentAccess();
};

dictionary CredentialData {
  required USVString id;
};

dictionary CredentialRequestOptions {
  CredentialMediationRequirement mediation = "optional";
  DOMString uiMode;
  AbortSignal signal;
};

enum CredentialMediationRequirement {
  "silent",
  "optional",
  "conditional",
  "required"
};

enum CredentialUiMode {
  "immediate"
};

dictionary CredentialCreationOptions {
  CredentialMediationRequirement mediation = "optional";
  AbortSignal signal;
};

[Exposed=Window,
 SecureContext]
interface PasswordCredential : Credential {
  constructor(HTMLFormElement form);
  constructor(PasswordCredentialData data);
  readonly attribute USVString password;
};
PasswordCredential includes CredentialUserData;

partial dictionary CredentialRequestOptions {
  boolean password = false;
};

dictionary PasswordCredentialData : CredentialData {
  USVString name;
  USVString iconURL;
  required USVString origin;
  required USVString password;
};

typedef (PasswordCredentialData or HTMLFormElement) PasswordCredentialInit;

partial dictionary CredentialCreationOptions {
  PasswordCredentialInit password;
};

[Exposed=Window,
 SecureContext]
interface FederatedCredential : Credential {
  constructor(FederatedCredentialInit data);
  readonly attribute USVString provider;
  readonly attribute DOMString? protocol;
};
FederatedCredential includes CredentialUserData;

dictionary FederatedCredentialRequestOptions {
  sequence<USVString> providers;
  sequence<DOMString> protocols;
};

partial dictionary CredentialRequestOptions {
  FederatedCredentialRequestOptions federated;
};

dictionary FederatedCredentialInit : CredentialData {
  USVString name;
  USVString iconURL;
  required USVString origin;
  required USVString provider;
  DOMString protocol;
};

partial dictionary CredentialCreationOptions {
  FederatedCredentialInit federated;
};

问题索引

和 Tobie/Dominic 讨论一下这里以及 接口对象 相关的内容,以及 § 2.5.1 请求凭证等部分。我不确定这里的术语是否用得准确。也许应该用 接口原型对象?↵
这可能不是正确的模型。最好能支持希望同时接受用户名/密码和 webauthn 类型凭证的网站,但又不强制那些使用前者且希望保持登录状态的用户每次都要选取凭证。↵
在此补充一些关于何时以及如何使用该 API 的思考,特别是关于 mediation 的部分。 [w3c/webappsec Issue #290] ↵
说明通过 fetch() 并使用 FormData 作为 body 提交凭证时的编码限制。↵
✔MDN

Credential/id

In all current engines.

Firefox60+Safari13+Chrome51+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

Credential/type

In all current engines.

Firefox60+Safari13+Chrome51+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

Credential

In all current engines.

Firefox60+Safari13+Chrome51+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

CredentialsContainer/create

In all current engines.

Firefox60+Safari13+Chrome60+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

CredentialsContainer/get

In all current engines.

Firefox60+Safari13+Chrome51+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

CredentialsContainer/preventSilentAccess

In all current engines.

Firefox60+Safari13+Chrome60+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

CredentialsContainer/store

In all current engines.

Firefox60+Safari13+Chrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

CredentialsContainer

In all current engines.

Firefox60+Safari13+Chrome51+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

FederatedCredential/FederatedCredential

In only one current engine.

FirefoxNoneSafariNoneChrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

FederatedCredential/protocol

In only one current engine.

FirefoxNoneSafariNoneChrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

FederatedCredential/provider

In only one current engine.

FirefoxNoneSafariNoneChrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

FederatedCredential

In only one current engine.

FirefoxNoneSafariNoneChrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
✔MDN

Navigator/credentials

In all current engines.

Firefox60+Safari13+Chrome51+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

PasswordCredential/PasswordCredential

In only one current engine.

FirefoxNoneSafariNoneChrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

PasswordCredential/iconURL

In only one current engine.

FirefoxNoneSafariNoneChrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

PasswordCredential/name

In only one current engine.

FirefoxNoneSafariNoneChrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

PasswordCredential/password

In only one current engine.

FirefoxNoneSafariNoneChrome60+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
⚠MDN

PasswordCredential

In only one current engine.

FirefoxNoneSafariNoneChrome51+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?