凭证管理 一级

W3C 工作草案,

关于本文档的更多详情
此版本:
https://www.w3.org/TR/2024/WD-credential-management-1-20240813/
最新发布版本:
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 技术报告索引 https://www.w3.org/TR/ 查阅。

本文档由 Web 应用安全工作组推荐流程 发布为工作草案。本文档旨在成为 W3C 推荐标准。

(存档) 公开邮件列表 public-webappsec@w3.org (见 说明) 是讨论本规范的首选渠道。 发送电子邮件时, 请在主题中加入 “credential-management”, 推荐格式如下: “[credential-management] …评论摘要…

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

本文档由 Web 应用安全工作组 编写。

本文档由遵循 W3C 专利政策 的工作组产生。 W3C 维护着 公开的专利披露清单,记录了与本组交付物相关的专利披露; 该页面还包含专利披露说明。 若个人实际知晓某专利且认为其中包含必要声明,则须根据 W3C 专利政策第 6 节 披露相关信息。

本文档受 2023 年 11 月 3 日 W3C 流程文件 管辖。

1. 简介

本节为非规范性内容。

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

autocomplete 属性为网站提供了一种声明式机制,可以与用户代理协作,通过将特定字段标记为"username"或"password",提升用户代理检测和填充登录表单的能力。用户代理还实现了各种检测启发式方法,以适应那些未在标记中提供此类细节的网站。

虽然这种启发式与声明式结合的检测方式相对有效,但现状仍存在一些检测上的重大缺口。拥有非常规登录机制的网站(例如通过 XMLHttpRequest [XMLHTTPREQUEST] 提交凭证)难以可靠检测,越来越多用户希望通过联合身份提供者进行认证也是如此。让网站能更直接地与用户代理的凭证管理器交互,一方面可以提升凭证管理器的准确性,另一方面也能帮助用户实现联合登录。

这些使用场景将在 § 1.1 使用场景凭证管理:使用场景与需求 中进一步探讨;本规范试图通过定义一个凭证管理器 API 来满足上述文档中的诸多需求,网站可用其请求用户的凭证,并在用户成功登录时请求用户代理持久化这些凭证。

注意: 此处定义的 API 有意保持简洁,旨在为现有用户代理实现的凭证管理器提供接口,并不直接提供认证功能。这一功能在当前阶段非常有价值,且对厂商和作者的要求都不高。当然,还可以做很多其他提升,详见 § 9 未来工作,其中列举了一些暂未实现但可在未来 API 迭代中探索的思路。

1.1. 使用场景

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

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

2. 核心 API

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

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

某个 凭证对于特定 源(origin) 是“有效”的,指的是该凭证可被该源接受用于认证。即使某一时刻的凭证有效,UA 也不能假定未来该凭证仍然有效,原因有以下几点:

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

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

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

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

2.1. 基础设施

用户代理必须在内部提供一个 凭证存储,即厂商自定义的、不透明的存储机制,用于记录哪些凭证有效的。它为凭证的访问和持久化提供如下能力:

  1. 存储凭证以备后续检索。该操作接收一个 凭证,并将其插入 凭证存储

  2. 检索凭证列表。该操作接收一个任意过滤条件,返回符合条件的凭证集合。

  3. 修改凭证。该操作接收一个 凭证,并覆盖 凭证凭证存储中的状态。

此外,凭证存储应为每个 防止静默访问 标志(默认设为 true,除非另有规定)进行维护。当某个源的标志为 true 时,该源 需要用户介入

注意: 用户介入的重要性详见 § 5 用户介入

注意: 凭证存储是用户代理实现本规范 API 的内部细节,不直接暴露给网页。其他文档可为特定凭证类型定义更多能力。

本规范依赖 Infra 标准,使用其算法和基础概念 [INFRA]

每个环境设置对象都有一个 激活凭证类型集合(有序集合),初始为空。

2.1.1. 基础设施算法

2.1.1.1. 与其祖先同源

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

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

  2. documentsettings相关全局对象关联的 Document

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

  4. originsettings

  5. navigabledocument节点可导航对象

  6. navigable 有非空 父级 时:

    1. navigablenavigable父级

    2. 如果 navigable活动文档origin 不同源,返回 false

  7. 返回 true

2.1.2. 凭证类型注册表

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

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

凭证类型
(按字母顺序)
选项成员标识符 适用接口对象 获取权限策略 创建权限策略 规范 申请者联系方式
digital-credential digital DigitalCredential digital-credentials-get null [DIGITAL-CREDENTIALS] WICG
federated federated FederatedCredential null null 本规范:§ 4 联合凭证 W3C
identity identity IdentityCredential identity-credentials-get null [FEDCM] W3C
otp otp OTPCredential otp-credentials null [WEB-OTP] WICG
password password PasswordCredential null null 本规范:§ 3 密码凭证 W3C
public-key publicKey PublicKeyCredential publickey-credentials-get publickey-credentials-create [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();
  static Promise<undefined> willRequestConditionalCreation();
};
id, 类型为 USVString,只读

凭证的标识符。每种凭证类型对于标识符的要求各不相同。例如,对于用户名/密码对,标识符可能代表用户名。

type, 类型为 DOMString,只读

该属性的 getter 返回对象的接口对象[[type]] 槽的值,用于指定该对象代表的凭证类型

isConditionalMediationAvailable()

返回一个 Promise, 如果且仅当用户代理支持针对该凭证类型的请求调解conditional 方式时,promise 解析为 true,否则为 false

Credential 的默认实现 isConditionalMediationAvailable()

  1. 返回 一个解析为 false 的 promise。

支持 conditional 调解的凭证类型规范必须显式重写此函数,使其 解析true

注意: 如果未定义该方法,则该 凭证类型不支持 conditional 调解。

willRequestConditionalCreation()

返回一个 Promise, 当用户代理注册了依赖方使用 conditional 方式创建凭证的意图后,该 promise 解析。

Credential 的默认实现 willRequestConditionalCreation()

  1. 返回 一个解析为 undefined 的 promise。

注意: 如果未定义该方法,则该 凭证类型不支持凭证创建的 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 可能在其上有效

2.2.1. Credential 内部方法

Credential 接口对象包含多个内部方法,用于检索和存储 Credential 对象,默认实现为本节所述的“空操作”。

除非另有说明,为继承自 Credential 的接口创建的每个 接口对象,都必须为这些内部方法至少提供一种实现,并根据 凭证类型的需要,覆盖 Credential 的默认实现。例如,见 § 3.2 PasswordCredential 接口§ 4.1 FederatedCredential 接口,以及 [WEBAUTHN]

2.2.1.1. [[CollectFromCredentialStore]] 内部方法
[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 被调用时需传入 originCredentialRequestOptions 和一个布尔值(仅当调用方的 环境设置对象与其祖先同源时为 true)。 该算法从用户代理的 凭证存储 中返回与给定选项匹配的 Credential 对象集合。如果没有匹配的 Credential 对象,则返回空集合。

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

  1. 返回空集合。

2.2.1.2. [[DiscoverFromExternalSource]] 内部方法
[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)并行方式被调用,参数为 originCredentialRequestOptions 对象, 及一个布尔值(仅当调用方的 环境设置对象与其祖先同源时为 true)。 若能根据给定选项返回凭证,则返回一个 Credential; 若无凭证可用,则返回 null;若发现失败(如选项不正确),则抛出错误(例如会抛出 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)并行方式被调用,参数为 originCredentialCreationOptions 以及一个布尔值(仅当调用方的 环境设置对象与其祖先同源时为 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 仅在 安全上下文中暴露。

[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() 时,用户代理必须返回在 options 上执行 请求凭证 的结果。

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

当调用 store() 时,用户代理必须返回在 credential 上执行 存储凭证 的结果。

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

当调用 create() 时,用户代理必须返回在 options 上执行 创建凭证 的结果。

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

当调用 preventSilentAccess() 时,用户代理必须返回在 当前设置对象上执行 防止静默访问 的结果。

注意:此处的目的是作为源发出的用户已注销的信号。也就是说,在用户点击“注销”按钮后,站点会更新用户会话信息,并调用 navigator.credentials.preventSilentAccess()。这将设置防止静默访问标志,意味着下次用户访问时凭证不会自动返回给页面。

注意:此函数以前称为 requireUserMediation(),现应视为弃用。

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

2.3.1. CredentialRequestOptions 字典

为了通过 Credentialget() 方法检索凭证, 调用方需要在 CredentialRequestOptions 对象中指定一些参数。

注意: CredentialRequestOptions 字典是一个扩展点。当有新的凭证类型引入且需要选项时,这些字典类型将会添加到该字典中,以便可以传递给请求。参见 § 8.2 扩展点

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

该属性指定了特定凭证请求的调解要求。每个枚举值的含义如下文 CredentialMediationRequirement 所述。 处理细节见 § 2.5.1 请求凭证

signal, 类型为 AbortSignal

该属性允许开发者中止正在进行的 get() 操作。 被中止的操作可能正常完成(通常是如果中止信号在操作完成后才收到),也可能因 中止原因而被拒绝。

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

unmediated 应被视为弃用,新代码应使用 mediation 属性。

给定 CredentialCreationOptionsCredentialRequestOptionsoptions),其 相关凭证接口对象 是按如下方式收集的一组 接口对象

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

  1. settings当前设置对象

  2. relevant interface objects 为一个 集合

  3. 遍历 options 的每个 optionKeyoptionValue

    1. credentialInterfaceObject适用接口对象(在 settings全局对象上)其 选项成员标识符optionKey

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

    3. credentialInterfaceObject 添加到 relevant interface objects 中。

  4. 返回 relevant interface objects

给定 CredentialRequestOptionsoptions),若以下步骤返回 true,则称其 可先验匹配(matchable a priori
  1. 遍历 options相关凭证接口对象

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

  2. 返回 true

注意:执行 get(options) 时, 仅当所提供的 CredentialRequestOptions可先验匹配时,才会返回无需 用户介入的凭证。如果请求的凭证类型可能需要从外部服务发现(如 OAuth 令牌、安全密钥认证器等),则需要 用户介入来引导发现过程(如选择联合身份提供方、BTLE 设备等)。

2.3.2. 调解要求

通过 get(options)create(options) 发起请求时, 开发者可通过选择合适的 CredentialMediationRequirement 枚举值,为每次请求设置 用户介入的要求。

注意:§ 5 用户介入章节有更多关于该概念的介绍,以及其对用户代理处理特定源请求的影响。

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

对该操作抑制用户介入。如果可以在无需用户参与的情况下完成操作,则非常好。如果需要用户参与,则操作会返回 null,而不是让用户介入。

注意:此用法旨在支持 “保持我登录此站点”场景,即开发者希望在用户应自动登录时静默获取凭证,只有当用户主动选择登录时才弹出登录提示,不打扰用户。

optional

如果可以在无需用户介入的情况下交付凭证,则会交付。如果需要用户介入,则用户代理会让用户参与决策。

注意:这是 get() 的默认行为,适用于开发者确信用户期望开始登录操作的场景。例如用户刚点击“登录”时,看到 凭证选择器不会感到意外或困惑。

conditional

对于 get(), 发现的凭证会在一个非模态对话框中展示给用户,并标明请求凭证的 。如果用户在对话框外操作,对话框会关闭且 Promise 不会被解析或拒绝,也不会显示错误。如果用户选择了某个凭证,则返回该凭证。防止静默访问标志被视为 true,无论实际值为何:conditional 行为总会涉及某种 用户介入(如发现相关凭证)。

如果未发现凭证,用户代理可根据凭证类型提示用户采取操作(如插入含凭证的设备)。无论哪种情况,get() 方法不应立即以 null 解析,以避免网站获知没有可用凭证。

仅当所有 相关凭证接口都重写了 isConditionalMediationAvailable() 返回一个解析为 truePromise 时,网站才能向 get() 传递 conditional

对于 create(), 如果用户先前已同意创建凭证且用户代理知晓近期已介入认证,则 create() 可在无需额外模态交互的情况下解析。如果用户代理最近未介入认证或无创建凭证同意,则调用必须抛出 “NotAllowedErrorDOMException

required

即使某源未设置 防止静默访问标志,用户代理也不会在无 用户介入的情况下交付凭证。

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

2.3.2.1. 示例
MegaCorp, Inc. 希望在可能时无缝登录用户。可以通过在着陆页加载时为所有未登录用户调用 get(),并传入 mediation 设置为 "silent"。 这样可确保已选择无需用户介入的用户(见 § 5.2 需要用户介入)会自动登录,未选择该行为的用户不会被突然弹出的 凭证选择器打扰:
window.addEventListener('load', async () => {
  const credentials = await navigator.credentials.get({
    ...,
    mediation: 'silent'
  });
  if (credentials) {
    // 太棒了!用这些凭证让用户登录吧!
  }
});
当用户点击“登录”时,MegaCorp, Inc. 希望为其提供最流畅的体验。如果用户已选择无需 用户介入登录,且用户代理能明确选出凭证,则直接登录;否则会展示 凭证选择器
document.querySelector('#sign-in').addEventListener('click', async () => {
  const credentials = await navigator.credentials.get({
    ...,
    mediation: 'optional'
  });
  if (credentials) {
    // 太棒了!用这些凭证让用户登录吧!
  }
});

注意:MegaCorp, Inc. 也可以不传 mediation,因为默认值为 "optional"。

MegaCorp, Inc. 希望在执行敏感操作前要求用户重新认证。即使用户选择无需 用户介入登录,MegaCorp, Inc. 也可以通过将 get()mediation 设置为 "required" 强制要求用户介入:

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

document.querySelector('#important-form').addEventListener('submit', async () => {
  const credentials = await navigator.credentials.get({
    ...,
    mediation: 'required'
  });
  if (credentials) {
    // 验证凭证是否允许访问,无效则取消提交。
  } else {
    e.preventDefault();
  }
});
MegaCorp, Inc. 希望支持同时登录多个用户账号。为确保用户有机会选择不同凭证,可以通过将 get()mediation 设置为 "required" 保证点击“添加账号”按钮不会自动返回凭证:
document.querySelector('#switch-button').addEventListener('click', e => {
  var c = await navigator.credentials.get({
    ...,
    mediation: 'required'
  });
  if (c) {
    // 用 |c| 登录用户。
  }
});

2.4. CredentialCreationOptions 字典

为了通过 Credentialcreate() 方法创建凭证, 调用方需要在 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

请求凭证算法接受一个 CredentialRequestOptionsoptions),并返回一个 Promise ,如果能明确获取凭证则解析为 Credential,否则解析为 null

  1. settings当前设置对象

  2. 断言:settings安全上下文

  3. documentsettings相关全局对象关联文档

  4. 如果 document 不是 完全激活状态,则返回 一个被拒绝的 promise,原因为 "InvalidStateError" DOMException

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

  6. interfacesoptions相关凭证接口对象

  7. 如果 interfaces空集合,则返回 一个被拒绝的 promise,原因为 "NotSupportedError" DOMException

  8. 遍历 interfaces 的每个 interface

    1. 如果 options.mediationconditionalinterface 不支持 conditional 用户介入,则返回 一个被拒绝的 promise,原因为 "TypeError" DOMException

    2. 如果 settings激活凭证类型 包含 interface[[type]], 则返回 一个被拒绝的 promise,原因为 "NotAllowedError" DOMException

    3. interface[[type]] 添加到 settings激活凭证类型中。

  9. originsettings

  10. sameOriginWithAncestorstrue,如果 settings 与其祖先同源,否则为 false

  11. 遍历 options相关凭证接口对象

    1. permissioninterface[[type]] 获取权限策略

    2. 如果 permission 为 null,跳过。

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

  12. p一个新的 promise

  13. 并行运行以下步骤:

    1. credentials从凭证存储收集凭证的结果,参数为 originoptionssameOriginWithAncestors

    2. 如果 credentials异常拒绝 p,原因为 credentials

    3. 如果以下所有条件成立,则解析 pcredentials[0] 并跳过剩余步骤:

      1. credentials长度为 1

      2. origin需要用户介入

      3. options 可先验匹配

      4. options.mediation 不是 "required"。

      5. options.mediation 不是 "conditional"。

      这也许不是最佳模型。如果一个网站希望同时接受用户名/密码和 webauthn 类型凭证,但不想强制使用选择器,对于只用前者且希望保持登录状态的用户会更友好。

    4. 如果 optionsmediation 为 "silent", 解析 pnull,并跳过剩余所有步骤。

    5. result请求用户选择凭证的结果,参数为 optionscredentials

    6. 如果 result接口对象

      1. result 设为执行 result[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 结果, 参数为 originoptionssameOriginWithAncestors

        如果抛出 异常

        1. e 为抛出的 异常

        2. globalDOM 操作任务源排队一个任务以运行以下子步骤:

          1. 拒绝 p,原因为 e

        3. 终止这些子步骤。

    7. 断言:resultnull,或为 Credential

    8. 如果 resultCredential解析 presult

    9. 如果 resultnulloptions.mediation 不是 conditional解析 presult

      注意:如果 options.mediationconditional 且发现凭证为 null, promise p 不会被解析。

  14. p 被解决时,执行:

    1. 遍历 interfaces 的每个 interface

      1. 移除 interface[[type]]settings激活凭证类型中。

  15. 返回 p

2.5.2. 从凭证存储收集 Credential

给定一个 originorigin), 一个 CredentialRequestOptionsoptions),以及一个仅当调用上下文 与其祖先同源时为 true 的布尔值 (sameOriginWithAncestors),用户代理可以 从凭证存储收集 Credential, 返回一组本地存储且与 options 的筛选条件匹配的 Credential 对象。如果没有可用的 Credential 对象,返回集合为空:

  1. possible matches 为一个空集合。

  2. 遍历 options相关凭证接口对象 的每个 interface

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

    2. 断言:r 是一组 接口对象列表。

    3. 遍历 r 的每个 c

      1. c 添加到 possible matches

  3. 返回 possible matches

2.5.3. 存储 Credential

存储 Credential 算法接受一个 Credentialcredential),并返回一个 Promise ,该 Promise 在对象持久化到 凭证存储后解析。

  1. settings当前设置对象

  2. 断言:settings安全上下文

  3. 如果 settings相关全局对象关联文档不是 完全激活状态, 则返回 一个被拒绝的 promise,原因为 "InvalidStateError" DOMException

  4. sameOriginWithAncestorstrue,如果 当前设置对象 与其祖先同源,否则为 false

  5. p一个新的 promise

  6. 如果 settings激活凭证类型 包含 credential[[type]], 则返回 一个被拒绝的 promise,原因为 "NotAllowedError" DOMException

  7. credential[[type]] 添加到 settings激活凭证类型中。

  8. 并行运行以下步骤:

    1. 执行 credential接口对象[[Store]](credential, sameOriginWithAncestors) 内部方法,参数为 credentialsameOriginWithAncestors

      如果抛出 异常

      1. e 为抛出的 异常

      2. globalDOM 操作任务源排队一个任务以运行以下子步骤:

        1. 拒绝 p,原因为 e

      否则,解析 pundefined

  9. p 被解决时,执行:

    1. 移除 credential[[type]]settings激活凭证类型中。

  10. 返回 p

2.5.4. 创建 Credential

创建 Credential算法接受一个 CredentialCreationOptionsoptions),并返回一个 Promise ,如果可以根据所提供的选项创建凭证,则解析为 Credential,否则解析为 null。在异常情况下, Promise 可能会因相应异常而拒绝:

  1. settings当前设置对象

  2. 断言:settings安全上下文

  3. globalsettings全局对象

  4. document相关全局对象关联文档

  5. 如果 document 不是 完全激活状态,则返回 一个被拒绝的 promise,原因为 "InvalidStateError" DOMException

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

  7. interfaces集合,其中元素为 options相关凭证接口对象

  8. 如果出现如下任一情况,则返回 一个被拒绝的 promise NotSupportedError

    1. global 没有 关联文档

    2. interfaces长度大于 1。

      注意:未来可能会放宽此限制,让用户代理帮助用户在多种凭证类型中选择,支持“注册”场景。当前仅限字典为单一条目。

  9. 遍历 interfaces 的每个 interface

    1. permissioninterface[[type]] 创建权限策略

    2. 如果 permission 为 null,跳过。

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

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

  11. typeinterfaces[0] 的 [[type]]

  12. 如果 settings激活凭证类型 包含 type,则返回 一个被拒绝的 promise,原因为 "NotAllowedError" DOMException

  13. type 添加到 settings激活凭证类型中。

  14. originsettings

  15. p一个新的 promise

  16. 并行运行以下步骤:

    1. r 为执行 interfaces[0] 的 [[Create]](origin, options, sameOriginWithAncestors) 内部方法,参数为 originoptionssameOriginWithAncestors 的结果。

      如果抛出 异常

      1. e 为抛出的 异常

      2. globalDOM 操作任务源排队一个任务以运行以下子步骤:

        1. 拒绝(Reject) p,原因为 e

      3. 终止这些子步骤。

    2. 如果 rCredentialnull解析 p,值为 r,并终止这些子步骤。

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

    4. globalDOM 操作任务源排队一个任务以运行以下子步骤:

      1. 解析(Resolve) p,其结果为在 global 上对 promise-calling r 的调用。

  17. p 被解决时,执行:

    1. 移除 typesettings激活凭证类型中。

  18. 返回 p

2.5.5. 防止静默访问

防止静默访问算法接受一个环境设置对象settings),并返回一个 Promise ,该 Promise 在 防止静默访问标志持久化到凭证存储后解析。

  1. originsettings

  2. 如果 settings相关全局对象关联文档不是 完全激活状态, 则返回 一个被拒绝的 promise,原因为 "InvalidStateError" DOMException

  3. p一个新的 promise

  4. 并行运行以下步骤:

    1. 凭证存储中设置 origin防止静默访问标志

    2. 解析 p,值为 undefined

  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" 事件的 PasswordCredential 构造一个新的 HTMLFormElement,
      // 会自动抓取带有 "username" 和 "current-password" autocomplete 属性的字段值:
      var c = new PasswordCredential(e.target);

      // fetch 表单的 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 公司允许用户通过异步 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" 事件的 PasswordCredential 构造一个新的 HTMLFormElement,
    // 会自动抓取带有 "username" 和 "new-password" autocomplete 属性的字段值:
    var c = new PasswordCredential(e.target);

    // fetch 表单的 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]] 的内部槽, 其值为 "credential store"。

PasswordCredential(form)

该构造器接受一个 HTMLFormElementform),并执行如下步骤:

  1. origin当前设置对象

  2. r 为执行 通过 HTMLFormElement 创建 PasswordCredential,参数为 formorigin 的结果。

  3. 如果 r异常抛出 r

    否则返回 r

PasswordCredential(data)

该构造器接受一个 PasswordCredentialDatadata),并执行如下步骤:

  1. r 为执行 通过 PasswordCredentialData 创建 PasswordCredential,参数为 data 的结果。

  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) 被调用时,参数为 originorigin)、CredentialRequestOptionsoptions), 以及一个仅当调用上下文与其祖先同源时为 true 的布尔值(sameOriginWithAncestors)。 该算法会从 凭证存储返回一组 Credential 对象。如果没有匹配的 Credential 对象可用,则返回集合为空。

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

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

  2. 如果 sameOriginWithAncestorsfalse,则抛出 "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) 被调用时,参数为 originorigin)、CredentialCreationOptionsoptions),以及一个仅当调用上下文 与其祖先同源时为 true 的布尔值(sameOriginWithAncestors)。 该算法在可以创建时返回一个 PasswordCredential,否则返回 nullCredentialCreationOptions 字典必须有一个 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) 被调用时,传入一个 PasswordCredentialcredential),以及一个仅当调用上下文与其祖先同源时为 true 的布尔值 (sameOriginWithAncestors)。当 credential 持久化到 凭证存储后,该算法返回 undefined

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

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

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

  2. 如果用户代理的 凭证存储中已包含一个 PasswordCredentialstored),其 id 属性等于 credentialid, 且其 [[origin]] 槽与 credential[[origin]] 同源,则:

    1. 如果用户允许更新凭证(见 用户介入 的定义),则:

      1. 设置 storedpasswordcredentialpassword

      2. 设置 storednamecredentialname

      3. 设置 storediconURLcredentialiconURL

    否则,如果用户允许存储凭证(见 用户介入 的定义),则:

    1. 凭证存储中存储一个 PasswordCredential ,其属性如下:

      id

      credentialid

      name

      credentialname

      iconURL

      credentialiconURL

      [[origin]]

      credential[[origin]]

      password

      credentialpassword

3.3.4. 通过 HTMLFormElement 创建 PasswordCredential

通过 HTMLFormElement 创建 PasswordCredential,传入一个 HTMLFormElementform)和一个 originorigin),运行如下步骤。

注意: § 3.1.2 登录后确认§ 3.1.3 修改密码 提供了推荐用法示例。

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

  2. 设置 dataorigin 成员值为 origin 的值。

  3. formData 为执行 FormData 构造器,参数为 form 的结果。

  4. elements 为所有 可提交元素,其 表单拥有者form,按 树顺序排列。

  5. newPasswordObservedfalse

  6. 遍历 elements 的每个 field,执行如下步骤:

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

    2. namefieldname 属性值。

    3. 如果 formDatahas() 方法在 name 上执行结果为 false,则跳过至下一个 field

    4. 如果 fieldautocomplete 属性值包含一个或多个 自动填充细节标记tokens),则:

      1. 遍历 tokens 的每个 token

        1. 如果 token 是以下字符串之一的 ASCII 不区分大小写匹配,则执行相关步骤:

          "new-password"

          设置 datapassword 成员值为执行 formDataget() 方法,参数为 name 的结果,并将 newPasswordObserved 设为 true

          "current-password"

          如果 newPasswordObservedfalse, 设置 datapassword 成员值为执行 formDataget() 方法,参数为 name 的结果。

          注意:如果 newPasswordObservedfalse,则 new-password 字段优先于 current-password 字段。

          "photo"

          设置 dataiconURL 成员值为执行 formDataget() 方法,参数为 name 的结果。

          "name"
          "nickname"

          设置 dataname 成员值为执行 formDataget() 方法,参数为 name 的结果。

          "username"

          设置 dataid 成员值为执行 formDataget() 方法,参数为 name 的结果。

  7. c 为执行 通过 PasswordCredentialData 创建 PasswordCredential,参数为 data 的结果。如果抛出 异常,则重新抛出该异常。

  8. 断言:cPasswordCredential

  9. 返回 c

3.3.5. 通过 PasswordCredentialData 创建 PasswordCredential

通过 PasswordCredentialData 创建 PasswordCredential,给定一个 PasswordCredentialDatadata),运行如下步骤。

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

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

  3. 设置 c 的属性如下:

    password

    datapassword 成员值

    id

    dataid 成员值

    iconURL

    dataiconURL 成员值

    name

    dataname 成员值

    [[origin]]

    dataorigin 成员值。

  4. 返回 c

3.3.6. CredentialRequestOptionsPasswordCredential 的匹配

给定一个 CredentialRequestOptionsoptions),下述算法如果应将 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 身份提供者标识

protocol, 类型为 DOMString,只读,可为空

凭证的联合身份提供者协议(如 "openidconnect")。如果值为 null,则协议可由 provider 推断。

[[type]]

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

[[discovery]]

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

FederatedCredential(data)

该构造器接受一个 FederatedCredentialInitdata),执行如下步骤:

  1. r 为执行 通过 FederatedCredentialInit 创建 FederatedCredential, 参数为 data 的结果。如果抛出 异常,则重新抛出该异常。

  2. 返回 r

FederatedCredential 对象可通过向 navigator.credentials.create() 传递一个 FederatedCredentialInit 字典进行创建。

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(如 FederatedCredentialRequestOptionsproviders 数组或 FederatedCredentialprovider 属性)必须用该提供者用于登录的 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) 被调用时,参数为 originorigin)、CredentialRequestOptionsoptions), 以及一个仅当调用上下文与其祖先同源时为 true 的布尔值(sameOriginWithAncestors)。 该算法会从 凭证存储返回一组 Credential 对象。如果没有匹配的 Credential 对象可用,则返回集合为空。

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

  2. 如果 sameOriginWithAncestorsfalse,则抛出 "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) 被调用时,参数为 originorigin)、CredentialCreationOptionsoptions),以及一个仅当调用上下文与其祖先同源时为 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) 被调用时,传入一个 FederatedCredentialcredential),以及一个仅当调用上下文与其祖先同源时为 true 的布尔值 (sameOriginWithAncestors)。当 credential 持久化到 凭证存储后,该算法返回 undefined

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

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

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

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

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

    id

    credentialid

    name

    credentialname

    iconURL

    credentialiconURL

    [[origin]]

    credential[[origin]]

    provider

    credentialprovider

    protocol

    credentialprotocol

4.2.4. 通过 FederatedCredentialInit 创建 FederatedCredential

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

  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 向网页暴露凭证信息会对用户隐私产生诸多潜在影响。因此,用户代理必须在多个情景下让用户介入,以确保用户清楚地了解发生了什么,以及自己的凭证将与谁共享。

如果某个动作是在获得用户明确同意后发生的,我们称之为用户介入。同意可以通过用户直接与凭证选择器界面交互等方式表达。一般来说,用户介入动作会涉及向用户展示某种界面,要求他们做出决定。

如果某个动作是静默发生的,没有获得用户明确同意,则称为“无介入”。例如,如果用户将浏览器配置为对特定源持久授予凭证访问权限,则凭证可能会在未向用户展示请求界面的情况下被提供。

这里我们列出所有凭证类型通用的部分要求,但请注意,用户代理还有较大的自由度(它处于帮助用户的特权位置)。此外,特定的凭证类型可能有超出这里通用要求的额外要求。

5.1. 存储和更新凭证

凭证信息是敏感数据,用户必须能一直掌控这些信息的存储。意外存储凭证可能会导致用户本地设备上的个人资料意外地与某个在线身份关联。为降低意外风险:

  1. 凭证信息不应在没有用户介入的情况下被存储或更新。例如,用户代理可以在每次调用 store() 时弹出 “保存此凭证?” 对话框。

    如果用户代理选择以 “始终保存密码” 的方式为用户提供持久授权,则可以推断用户同意(不过我们建议用户代理应谨慎,可以只提供“始终保存生成的密码”或“始终保存此网站的密码”等更窄范围的选项)。

  2. 用户代理应在凭证被存储时通知用户。通知方式可以是地址栏中的图标或类似的位置。

  3. 用户代理必须允许用户手动移除已存储的凭证。此功能可以通过设置页面实现,也可以通过与上述通知交互实现。

5.2. 强制用户介入

默认情况下,所有用户介入都要求对所有进行,因为防止静默访问标志凭证存储中被设为 true。用户可以选择授予某个源对凭证的持久访问权限(例如“保持登录此网站”的选项),这会将该标志设为 false。这样,用户就会一直保持登录状态,这对可用性和便利性很有利,但也可能带来意外影响(比如用户代理在设备间同步该标志状态)。

为降低意外风险:

  1. 用户代理必须允许用户对某个源或所有源强制用户介入。此功能可以作为全局开关实现,覆盖每个源的防止静默访问标志,使其返回 false,或者对特定源(或特定源上的特定凭证)实现更细粒度的设置。

  2. 用户代理不得在没有用户介入的情况下将其防止静默访问标志设为 false。例如,凭证选择器(见 § 5.3 凭证选择)可以有一个复选框,用户可以切换以将凭证标记为该源无需介入即可使用,或者用户代理在凭证管理器的初始设置流程中询问用户默认设置。

  3. 用户代理必须在凭证被提供给某个源时通知用户。通知方式可以是地址栏中的图标或类似的位置。

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

5.3. 凭证选择

当对某个需要用户介入的源调用 get() 时,用户代理必须向用户请求共享凭证信息的权限。这应该以凭证选择器的形式出现,向用户展示可在该网站使用的凭证列表,允许用户选择一个提供给网站,也可以取消请求。

选择器的用户界面应以网站无法伪造的方式与网页 UI 区分开。例如,选择器可以覆盖用户代理的 UI,防止伪造。

选择器的用户界面必须包含请求凭证的源的标识。

选择器的用户界面应包含所有与请求凭证的源相关联的 Credential 对象。

用户代理可以在每个 Credential 对象上内部关联本文件未指定的附加信息,以增强选择器的实用性。例如,favicon 可帮助区分身份提供者等。任何额外信息都不得直接暴露给网页。

选择器的行为未在此定义:鼓励用户代理在用户教育和引导选择凭证方面进行 UI 创新。不过,选择器的接口如下:

用户代理可以请求用户选择 Credential,传入一个 CredentialRequestOptionsoptions)和一组来自凭证存储Credential 对象(本地发现的凭证)。

该算法返回 null(如果用户拒绝共享凭证),返回一个 Credential(如果用户选择了具体凭证),或者返回一个 Credential 接口对象(如果用户选择了凭证类型)。

选择器界面可以向用户展示 本地发现的凭证 列表,类似如下非规范草图:

A mockup of what a chooser might look like.

如果传入的 options 不是可先验匹配,选择器界面也可以展示 options相关凭证接口对象列表(未被显式凭证覆盖)。例如,站点接受 webauthn 类型认证器时,“安全密钥”可以以合适图标显示在选择器列表中。

还请注意,某些情况下用户代理可以直接跳过选择器。例如,如果唯一的相关凭证接口对象本身就需要用户交互,则用户代理可以直接返回该接口,并依赖其内部介入流程获得用户同意。

6. 安全注意事项

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

6.1. 跨域凭证访问

凭证属于敏感信息,用户代理在确定何时可以安全地与网站共享时需谨慎。最安全的做法是仅与保存凭证的精确源共享凭证。然而,这对 Web 来说可能过于严格:请考虑将功能分布在子域名上的网站,如 example.comadmin.example.com

为在减少用户不便与增强凭证安全之间折中,用户代理:

  1. 不得在 scheme 降级时在不同源之间共享凭证。例如,允许将 http://example.com/ 上保存的凭证用于 https://example.com/(鼓励开发者迁移到安全传输),但反过来则很危险。

  2. 可以使用 Public Suffix List [PSL],通过比较凭证的 可注册域名和调用 get() 的源,确定凭证的有效作用范围。即:在 https://www.example.com/ 调用 get() 时,可以向用户提供 https://admin.example.com/https://example.com/ 上保存的凭证,反之亦然。

  3. 在凭证的源与调用源不完全匹配时,不得在响应 get() 时直接提供凭证,除非经过用户介入。即 Credential 对象仅在源精确匹配时直接返回,否则通过选择器提供给用户。

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 强制用户介入 所述,如果用户选择自动登录网站,则用户代理会在网站请求时提供凭证。网站可通过调用 CredentialsContainerpreventSilentAccess() 方法,关闭某源的自动登录行为。

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

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

7. 隐私注意事项

7.1. 计时攻击

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

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

7.2. 选择器泄露

如果用户代理的凭证选择器显示由源提供的图片(例如 Credential 显示网站 favicon),则这些图片的请求不得与实例化选择器直接关联,以避免泄露选择器的使用。可以选择在保存或更新 Credential 时后台获取图片,并在该 Credential 生命周期内缓存它们。

这些图片必须以 credentials mode 为 "omit"、service-workers mode 为 "none"、clientnullinitiator 为空字符串、destination 为 "subresource" 进行获取。

此外,如果用户代理允许用户更改凭证关联的名称或图标,则对数据的更改不应暴露给网站(例如用户将两个凭证命名为“我的假账号”和“我的真账号”)。

7.3. 本地存储数据

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

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

8. 实现注意事项

本节为非规范内容。

8.1. 网站作者

这里可以补充关于 API 何时如何使用的建议,尤其是 mediation 相关内容。 [Issue #w3c/webappsec#290]

描述通过 fetch() 提交含 FormData 的凭证时的编码限制。

在为某个凭证类型进行特性检测时,建议开发者验证相关 Credential 的具体实现是否存在,而不是仅检查 navigator.credentals 是否存在。后者只能验证 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 使用 AbortController 和 AbortSignal 对象

    ExampleCredential[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors) 内部方法被调用时,参数为 originorigin)、CredentialRequestOptions(options)、以及仅当调用上下文与其祖先同源时为 true 的布尔值。该算法返回与所提供 options 匹配的 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(),其返回 a promise resolved with true

  8. § 2.1.2.1 注册条目要求和更新流程,为新的 "example" 凭证类型及其对应项添加注册表条目:

    注意:凭证类型 options 字典的options 成员标识符必须在 CredentialCreationOptionsCredentialRequestOptions 中一致,并且应与 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级(Content Security Policy Level 3). 2024年6月18日. WD. URL: https://www.w3.org/TR/CSP3/
[DIGITAL-IDENTITIES]
数字凭证(Digital Credentials). 草案社区组报告. URL: https://wicg.github.io/digital-identities/
[DOM]
Anne van Kesteren. DOM 标准(DOM Standard). 现行标准. URL: https://dom.spec.whatwg.org/
[FEDCM]
联合凭证管理 API(Federated Credential Management API). 草案社区组报告. URL: https://fedidcg.github.io/FedCM/
[FETCH]
Anne van Kesteren. Fetch 标准(Fetch Standard). 现行标准. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; 等. HTML 标准(HTML Standard). 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准(Infra Standard). 现行标准. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. 权限(Permissions). 2024年3月19日. WD. URL: https://www.w3.org/TR/permissions/
[PROMISES-GUIDE]
Domenic Denicola. Promise使用规范写作指南(Writing Promise-Using Specifications). 2018年11月9日. TAG Finding. URL: https://www.w3.org/2001/tag/doc/promises-guide
[PSL]
公共后缀列表(Public Suffix List). Mozilla 基金会.
[SECURE-CONTEXTS]
Mike West. 安全上下文(Secure Contexts). 2023年11月10日. CR. URL: https://www.w3.org/TR/secure-contexts/
[WEB-OTP]
WebOTP API. 草案社区组报告. URL: https://wicg.github.io/web-otp/
[WEBAUTHN-3]
Michael Jones; Akshay Kumar; Emil Lundberg. Web Authentication: 用于访问公钥凭证的API - 第3级(An API for accessing Public Key Credentials - Level 3). 2023年9月27日. WD. URL: https://www.w3.org/TR/webauthn-3/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准(Web IDL Standard). 现行标准. URL: https://webidl.spec.whatwg.org/
[XHR]
Anne van Kesteren. XMLHttpRequest 标准(XMLHttpRequest Standard). 现行标准. 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
[DIGITAL-CREDENTIALS]
Marcos Cáceres; Sam Goto. 数字凭证(Digital Credentials). URL: https://wicg.github.io/digital-credentials/
[SRI]
Devdatta Akhawe; 等. 子资源完整性(Subresource Integrity). 2016年6月23日. REC. URL: https://www.w3.org/TR/SRI/
[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级(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级(XMLHttpRequest Level 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();
  static Promise<undefined> willRequestConditionalCreation();
};

[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";
  AbortSignal signal;
};

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

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 请求 Credential 等部分。我不确定我的术语是否准确。接口原型对象,也许?
这也许不是正确的模型。如果有网站希望同时支持用户名/密码和 webauthn 风格凭证,而不会强制选择器弹出给使用前者并希望保持登录状态的用户,那会更好。
这里可以补充关于 API 何时如何使用的建议,尤其是 mediation 相关内容。 [Issue #w3c/webappsec#290]
描述通过 fetch() 提交含 FormData 的凭证时的编码限制。
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?