发布时间:2026 年 10 月 23 日
在 Chrome 155 及更高版本中,Digital Credentials API 签发功能默认处于启用状态,因此您无需再使用源试用令牌或标志即可使用该功能。Digital Credentials API 是一个传输层,可让网站连接到用户移动设备上安装的钱包应用。借助签发功能,签发方可以直接将新凭证(例如数字身份证件)添加到用户的钱包应用中。
背景
数字身份生态系统由三方组成:签发方、验证方(也称为依赖方)和持有方(钱包应用)。用户通常通过推送预配将数字身份证件添加到其钱包应用中,其中用户从签发机构的网站或应用开始签发。例如,大学网站(签发机构)上的学生可以将自己的数字学生证保存到其首选钱包应用(持有者)中。
为了使此模型成功,签发方需要一种安全可靠的方式来调用钱包应用以签发 ID。在此 API 推出之前,每个发卡机构都必须构建自己的方式来打开与其合作的钱包应用,通常使用自定义 URI scheme。发卡机构必须为每个钱包应用维护一个单独的机制,而用户也没有一致的方式来选择钱包应用。
数字凭证签发流程
为了简化此流程,Chrome 支持使用 W3C Digital Credentials API 进行签发。这是对 Chrome 141 中推出的呈现功能(与验证方共享数字凭证中请求的、选择性的数据)的补充。当签发方网站调用 navigator.credentials.create() 并传入包含签发请求的 digital 成员时,Chrome 会将该请求传递给操作系统(在 Android 上为 Credential Manager)。然后,操作系统会显示自己的界面,供用户选择钱包应用。该 API 可与 OpenID for Verifiable Credential Issuance (OpenID4VCI) 等业界标准协议搭配使用。签发支持两种流程:Android 上的同设备流程,以及从桌面设备开始并在 Android 设备上完成的跨设备流程。
Android 上的同一设备流程
在 Android 设备上,Chrome 会将签发请求传递给 Android Credential Manager,后者会显示设备上兼容的钱包应用列表。用户选择一个钱包,并同意添加凭证。然后,钱包应用会使用 OpenID4VCI 等签发协议从签发方获取凭证。
桌面设备上的跨设备流程
在桌面设备上,Chrome 会显示一个二维码。用户使用其 Android 设备扫描该代码,并在该设备上完成流程,如上一部分中所述。设备会使用蓝牙来确认彼此是否靠近,因此如果蓝牙处于关闭状态,Chrome 会要求用户开启蓝牙。Chrome 会使用相同的跨设备机制来呈现 Digital Credentials API。
使用 API 实现发卡
如需启动签发流程,请调用 navigator.credentials.create(),并在 digital 成员中传递签发请求。请通过点击处理程序或其他用户手势处理程序进行此调用,因为此调用需要瞬时用户激活。以下示例使用 OpenID4VCI 凭证提议和预授权代码流程:
// Call this function from a user gesture handler, for example, a button's
// click handler. The pre-authorized code is a short-lived, single-use code
// that your server generates for the signed-in user.
async function issueDigitalCredential(preAuthorizedCode) {
// Check for Digital Credentials API and protocol support.
if (!window.DigitalCredential ||
!DigitalCredential.userAgentAllowsProtocol('openid4vci-v1')) {
// Provide an alternative issuance method.
showAlternativeIssuanceFlow();
return;
}
// Construct a credential offer according to the OpenID4VCI
// specification. This example uses the format that the
// demo CMWallet supports.
const credentialOffer = {
credential_issuer: 'https://issuer.example',
credential_configuration_ids: ['org.iso.18013.5.1.mDL'],
grants: {
'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
'pre-authorized_code': preAuthorizedCode
}
},
// Metadata that the wallet uses to request and display the credential.
credential_issuer_metadata: {
credential_issuer: 'https://issuer.example',
credential_endpoint: 'https://issuer.example/openid4vci/credential',
nonce_endpoint: 'https://issuer.example/openid4vci/nonce',
credential_configurations_supported: {
'org.iso.18013.5.1.mDL': {
format: 'mso_mdoc',
doctype: 'org.iso.18013.5.1.mDL',
// Bind the credential to a key that the wallet generates.
cryptographic_binding_methods_supported: ['cose_key'],
proof_types_supported: {
jwt: { proof_signing_alg_values_supported: ['ES256'] }
},
credential_metadata: {
display: [{ name: 'Mobile driving license', locale: 'en-US' }],
// The claims that this credential contains. For a full list of
// standard mDL claims, see the ISO/IEC 18013-5 specification.
claims: [{
path: ['org.iso.18013.5.1', 'family_name'],
display: [{ name: 'Family name', locale: 'en-US' }]
}
// ... Add any other claims for your credential here.
]
}
}
}
},
// Metadata of the authorization server that issues access tokens.
authorization_server_metadata: {
issuer: 'https://issuer.example',
token_endpoint: 'https://issuer.example/openid4vci/token'
}
};
try {
const credential = await navigator.credentials.create({
digital: {
requests: [{
// The protocol identifier for OpenID4VCI 1.0.
protocol: 'openid4vci-v1',
// Pass the offer as an object, not as a JSON string.
data: credentialOffer
}]
}
});
// The wallet handled the request. credential.data is an object that
// contains the wallet's protocol-specific response. It is not the issued
// credential, and it does not necessarily mean that issuance is complete.
// Your server knows when the wallet retrieves the credential from your
// credential endpoint.
// Implement your custom logic here as needed.
showIssuanceStatus(credential);
} catch (error) {
switch (error.name) {
case 'NotAllowedError':
// The user canceled the flow, no wallet can handle the request, or
// the call does not have transient user activation.
break;
case 'NotSupportedError':
// The browser does not support Digital Credentials API issuance.
// Fall back to your custom alternative flow logic.
showAlternativeIssuanceFlow();
break;
case 'TypeError':
// The request is malformed, for example, data is not an object.
console.error('Invalid issuance request:', error);
break;
default:
// For example, AbortError or NetworkError.
console.error('Credential issuance failed:', error);
}
}
}
const addButton = document.querySelector('#add-to-wallet');
addButton.addEventListener('click', () => {
// Your server adds a pre-authorized code for the signed-in user
// to the button's data-pre-authorized-code attribute.
issueDigitalCredential(addButton.dataset.preAuthorizedCode);
});
在此示例中,showAlternativeIssuanceFlow() 和 showIssuanceStatus() 代表您自己的应用逻辑。
如需从跨域 iframe 调用 create(),嵌入页面必须允许 digital-credentials-create 权限政策功能。否则,调用会被拒绝,并返回 NotAllowedError:
<iframe src="https://issuer.example" allow="digital-credentials-create">
</iframe>
源试用后的变化
自源试用以来,API 方法和参数未发生变化。不过,以下变更在 Chrome 143 到 Chrome 155 之间发布,其中一些变更可能需要您更新代码。下表总结了这些变化,其后的部分对每项变化进行了说明。
| 领域 | 源试用 (Chrome 143) | Chrome 155 |
|---|---|---|
| 可用性 | 源试用令牌或 chrome://flags/#web-identity-digital-credentials-creation 标志 |
默认处于启用状态 |
| 用户激活 | 必需,但未消耗 | 每次调用 create() 时都需要并使用(Chrome 146) |
| 取消错误 | NetworkError |
NotAllowedError (Chrome 148) |
| 协议标识符 | Chrome 会将所有标识符传递给操作系统 | 无法识别的标识符会触发弃用警告 (Chrome 151) |
| 不透明源 | 允许 | 已通过 NotAllowedError 屏蔽 (Chrome 151) |
| 自动测试 | 不支持内置 | Chrome 开发者工具协议中的虚拟钱包 (Chrome 152) |
用户激活已被使用
从 Chrome 146 开始,根据规范要求,使用 digital 成员调用 create() 会消耗临时用户激活。在源试用期间,Chrome 会检查用户激活情况,但不会消耗激活。
因此,每次 create() 调用都需要有自己的用户手势,例如点击按钮。如果没有用户激活,promise 会被拒绝,并返回 NotAllowedError。调用后,window.open() 和其他需要用户激活的 API 无法使用相同的手势。
取消拒绝,原因为 NotAllowedError
从 Chrome 148 开始,当用户取消流程(例如,在 Android 上关闭钱包选择器或在桌面设备上关闭二维码对话框)时,create() 返回的 promise 会被拒绝,并显示 NotAllowedError。如果没有钱包应用可以处理请求,该 promise 也会拒绝并返回 NotAllowedError。
在源试用期间,这些情况会被拒绝,并返回 NetworkError。
如果您的代码通过检查 NetworkError 来检测取消,请将其更新为检查 NotAllowedError。
无法识别的协议标识符会触发警告
该规范为 OpenID4VCI 1.0 定义了一个颁发协议标识符 openid4vci-v1。Chrome 155 可识别 openid4vci-v1,也可识别 openid4vci。由于 openid4vci 不是规范的一部分,因此我们建议您使用 openid4vci-v1。
Chrome 会将带有任何协议标识符的请求传递给平台。不过,从 Chrome 151 开始,当网页使用无法识别的协议标识符调用 create() 时,开发者工具会显示弃用警告。Chrome 打算屏蔽带有无法识别的协议标识符的请求。如需了解状态,请参阅“Digital Credential API 中更具体的协议过滤”条目。
如需测试此行为,请启用 chrome://flags/#enable-experimental-web-platform-features。启用此标志后,create() 会忽略具有无法识别的协议标识符的请求,并在没有剩余请求时拒绝 TypeError。此外,DigitalCredential.userAgentAllowsProtocol() 仅针对已识别的协议返回 true。如果不使用该标志,则只会检查标识符的格式是否正确。
因此,请勿仅依赖 userAgentAllowsProtocol() 进行功能检测。
按照示例所示处理 NotSupportedError。
不透明来源会被屏蔽
从 Chrome 151 开始,如果具有不透明来源的文档(例如没有 allow-same-origin 令牌的沙盒化 iframe)调用 create(),则该方法会拒绝并抛出 NotAllowedError。
在源试用期间,Chrome 并未屏蔽这些调用。
使用虚拟钱包进行测试
从 Chrome 152 开始,您无需 Android 设备或钱包应用即可测试签发流程。Chrome DevTools Protocol (CDP) 中的实验性 DigitalCredentials.setVirtualWalletBehavior 命令实现了规范为自动化测试定义的虚拟钱包。虚拟钱包可以返回您提供的数据、拒绝请求(就像用户取消了流程一样),或让请求处于待处理状态以测试超时和中止情况。
以下 Puppeteer 测试会将虚拟钱包设置为响应 openid4vci-v1 请求,然后点击启动发卡的按钮:
const client = await page.createCDPSession();
await client.send('DigitalCredentials.setVirtualWalletBehavior', {
action: 'respond',
protocol: 'openid4vci-v1',
response: {}
});
// Clicking the button provides the user activation that create() requires.
await page.click('#add-to-wallet');
行业采用情况
我们看到 Digital Credentials API 签发功能已被采用。例如,德国金融机构 Sparkasse 使用 Digital Credentials API 让账号持有人将经过验证的年龄凭证保存到 Google 钱包。然后,账号持有人可以使用这些凭证在线上和线下验证自己的年龄。
试试看
在测试 Digital Credentials API 签发之前,请确保您具备以下条件:
- 请更新到 Chrome 155 或更高版本。
- 在 Android 设备上安装受支持的钱包应用,例如演示版 CMWallet。
- 两部设备均已开启蓝牙(仅限跨设备流程)。
如需测试跨设备流程,请执行以下操作:
- 在计算机上,打开演示版发卡机构网站。
- 点击添加按钮。
- 使用 Android 设备扫描二维码,然后按照屏幕上的说明操作。
如需测试同设备流程,请在 Android 设备上使用 Chrome 打开演示版发卡机构网站。
开发者资源
如需开始将 Digital Credentials API 签发功能集成到您的网站中,请使用以下资源:
- 阅读 W3C Web 身份和凭证采用社区群组提供的开发者指南。
- 阅读 W3C 数字凭证规范。
- 阅读 OpenID for Verifiable Credential Issuance 1.0 规范。
- 探索 CMWallet 演示版(一款适用于 Android 的开源演示版钱包应用)的源代码。
- 了解 Android 钱包应用如何处理来自 Credential Manager 的签发请求。
- 在 Android 设备上安装演示版 CMWallet,然后试用演示版发卡机构网站。
- 探索数字凭证请求构造函数演示,了解请求载荷是什么样的。
分享您的反馈
如需报告 bug 或请求功能,请在 Chromium bug 跟踪器中提交问题。