关于 WebAuthn 和实施通行密钥的开发者指南。下载 PDF 格式的速查表,或者在此网站一站式获取所有信息。
Lukas R.
创建: 2024年3月6日
更新: 2026年7月3日

本页由自动翻译生成。请阅读英文原文 此处.
免费下载完整的通行密钥速查表并获取所有技术解析。
终极通行密钥开发者指南
获取面向开发者的通行密钥参考指南,涵盖平台支持、浏览器行为、用户体验最佳实践和集成提示。

使用通行密钥的身份验证基于两个过程(也称为流程):注册(也称为证明阶段)和登录(也称为断言阶段)。
每个阶段都需要一个由服务器生成的随机质询,该质询由身份验证器签名后发送回 WebAuthn 服务器以验证用户身份。
在 Passkeys Debugger 中体验 passkey 流程。
注册流程使用两个核心对象:PublicKeyCredentialCreationOptions 和证明 (attestation)。
登录流程使用两个核心对象:PublicKeyCredentialRequestOptions 和断言 (assertion)。
查看实际有多少人在使用 passkeys。
对于使用通行密钥的注册和登录,主要有四个对象:
本节还将解释 authenticatorSelection 对象,该对象在 PublicKeyCredentialCreationOptions 中使用。
Igor Gjorgjioski
Head of Digital Channels & Platform Enablement, VicRoads
We hit 80% mobile passkey activation across 5M+ users without replacing our IDP.
See how VicRoads scaled passkeys to 5M+ users — alongside their existing IDP.
Read the case studyPublicKeyCredentialCreationOptions 是证明阶段(注册)的核心对象。它由 WebAuthn 服务器创建并返回。
{ "PublicKeyCredentialCreationOptions": { "rp": { "id": "passkeys.eu", "name": "Corbado Passkeys Demo" }, "user": { "displayName": "john.doe", "id": "dXNyLZ….DU10Tc", "name": "john@doe.com" }, "challenge": "888fix4Bus...pHHr3Y", "pubKeyCredParams": [ { "alg": -7, "type": "public-key" }, { "alg": -257, "type": "public-key" } ], "excludeCredentials": [], "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "userVerification": "required" }, "attestation": "none", "extensions": {} } }
该对象包含以下属性:
订阅我们的 Passkeys Substack,获取最新消息。
PublicKeyCredentialRequestOptions 是断言阶段(登录)的核心对象。它由 WebAuthn 服务器创建并返回。
{ "publicKeyCredentialRequestOptions": { "challenge": "pT7HMA-…dFPHk", "timeout": 500, "rpId": "passkeys.eu", "userVerification": "preferred", "allowCredentials": [], "extensions": [] } }
该对象包含以下属性:
在证明/注册流程中,身份验证器会返回此注册响应。您可以在 Passkeys Debugger 中自行尝试。
{ "authenticatorAttachment": "platform", "id": "JKZbixUfKN_aZtimefYT-OjH5dw", "rawId": "JKZbixUfKN_aZtimefYT-OjH5dw", "response": { "attestationObject": { "fmt": "none", "attStmt": {}, "authData": { "rpIdHash": "PpZrl-Wqt-OFfBpyy2SraN1m7LT0GZORwGA7-6ujYkM", "flags": { "userPresent": true, "userVerified": true, "backupEligible": true, "backupStatus": true, "attestedData": true, "extensionData": false }, "counter": 0, "aaguid": { "raw": "fbfc3007-154e-4ecc-8c0b-6e020557d7bd", "name": "iCloud Keychain" }, "credentialID": "JKZbixUfKN_aZtimefYT-OjH5dw", "credentialPublicKey": "pQECAyYgASFYIPWLalDzyxIDmAADvfK8iNM5To50kh7TyPH-teEz8RMdIlgg3D7bPIWQJ8z-WFn3zdYZzJw9c7mhPdmflQqD9vV7efA", "parsedCredentialPublicKey": { "keyType": "EC2 (2)", "algorithm": "ES256 (-7)", "curve": 1, "x": "9YtqUPPLEgOYAAO98ryI0zlOjnSSHtPI8f614TPxEx0", "y": "3D7bPIWQJ8z-WFn3zdYZzJw9c7mhPdmflQqD9vV7efA" } } }, "clientDataJSON": { "type": "webauthn.create", "challenge": "k2p6f-upzP_hc6NZvmMAxiI0VSTeQIeXXVRGW62LTj0", "origin": "https://www.passkeys-debugger.io", "crossOrigin": false }, "transports": ["hybrid", "internal"], "authenticatorData": "PpZrl-Wqt-OFfBpyy2SraN1m7LT0GZORwGA7-6ujYkNdAAAAAPv8MAcVTk7MjAtuAgVX170AFCSmW4sVHyjf2mbYpnn2E_jox-XcpQECAyYgASFYIPWLalDzyxIDmAADvfK8iNM5To50kh7TyPH-teEz8RMdIlgg3D7bPIWQJ8z-WFn3zdYZzJw9c7mhPdmflQqD9vV7efA", "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9YtqUPPLEgOYAAO98ryI0zlOjnSSHtPI8f614TPxEx3cPts8hZAnzP5YWffN1hnMnD1zuaE92Z-VCoP29Xt58A", "publicKeyAlgorithm": -7 }, "type": "public-key", "clientExtensionResults": {} }
证明包含一些重要组件,如 attestationObject、algorithm 和 transport 标志。
attestationObject 是一个 CBOR 编码对象,其中包含有关新创建的凭据、公钥和其他相关信息的数据:
详细了解扩展。
通行密钥是使用 COSE 算法生成的,在证明对象的 parsedCredentialPublicKey 的 algorithm 属性中指明了所使用的算法。
以下是相关的 COSE 算法的概述:
transports 属性表示身份验证器可通过哪些机制与客户端通信。一些常见的示例值组合包括:
在断言/登录流程中,身份验证器会返回此登录响应。您可以在 Passkeys Debugger 中自行尝试。
{ "id": "JKZbixUfKN_aZtimefYT-OjH5dw", "rawId": "JKZbixUfKN_aZtimefYT-OjH5dw", "type": "public-key", "authenticatorAttachment": "platform", "response": { "authenticatorData": { "rpIdHash": "PpZrl-Wqt-OFfBpyy2SraN1m7LT0GZORwGA7-6ujYkM", "flags": { "userPresent": true, "userVerified": true, "backupEligible": true, "backupStatus": true, "attestedData": false, "extensionData": false }, "counter": 0 }, "clientDataJSON": { "type": "webauthn.get", "challenge": "GCVkITWbe2l2dttsn_DgJYvH9QPHPDo0ygWgcgI6B7U", "origin": "https://www.passkeys-debugger.io", "crossOrigin": false, "other_keys_can_be_added_here": "do not compare clientDataJSON against a template. See https://goo.gl/yabPex" }, "signature": "MEQCIA-orC8N2KKWOxyY17BWP8lB-Be5to9btXRnJZf2SLhXAiBGxJe5Eu5LwOTbsyzAYmIXHOhlC3pN7s7Q1fRLvEW57g", "userHandle": "_FKz1uwqmR_3yGq6hJntzoIFwFC_d1u_53YRELh0KlE" } }
断言包含一些重要组件,如标志 (flags)、签名 (signature) 和 userHandle。
以下是最重要标志及其组合的概述:
签名用于验证尝试登录的用户是否确实拥有私钥。签名是通过将 authenticatorData 和 clientDataHash(即 ClientDataJSON 的 SHA-256 版本)拼接在一起,并使用私钥(在身份验证器中)对结果进行签名来创建的。若要使用公钥进行验证,我们也拼接 authenticatorData 和 clientDataHash。如果验证结果返回 true,则身份验证成功。
userHandle 是实际的 user_id。在第 4.1 数据库架构 节中了解有关 user_id 的更多信息。
authenticatorSelection 对象允许服务器指示身份验证器和凭据创建的设置,包括以下值:
常驻密钥(也称为可发现凭据): 常驻密钥存储在身份验证器上,并在身份验证期间检索。以此方式,客户端可以发现可能的密钥列表,这就是 [Conditional UI](/blog/user-transition-passkeys-> conditional-ui) 需要常驻密钥的原因。 非常驻密钥(也称为不可发现凭据): 如果是非常驻密钥,凭据 ID 将存储在服务器上,并在身份验证期间提供。凭据 ID 是一种不透明标识符,其内部结构因实施而异——身份验证器可以直接存储私钥、使用加密的密钥包装,或从内部机密中派生密钥。确切的机制因身份验证器实现而异。
**警告:**如果设置为“Preferred”,则用户或其设备可以跳过身份验证过程中的用户验证(阅读此文章了解更多信息)。
当用户已向依赖方注册常驻密钥时,Conditional UI(通行密钥自动填充)会在选择下拉列表中向用户显示可用的通行密钥。它提高了通行密钥的可用性,但需要额外的开发工作,并且并非在所有操作系统/浏览器组合中可用。
像常规登录一样,Conditional UI 也使用对象 PublicKeyCredentialRequestOptions 和断言
Conditional UI 尚未在所有操作系统和浏览器组合上可用。以下是当前浏览器覆盖范围的概述(2024 年 3 月):
如需最新概述,请访问此网站。
Conditional UI 方法的完整、最简代码如下所示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Conditional UI</title> </head> <body> <input type="text" id="username" autocomplete="username webauthn" /> <script> async function passkeyLogin() { try { // 从 WebAuthn 服务器检索请求选项(包括质询) let options = await WebAuthnClient.getPublicKeyRequestOptions(); const credential = await navigator.credentials.get({ publicKey: options.publicKeyCredentialRequestOptions, mediation: "conditional", }); const userData = await WebAuthnClient.sendSignedChallenge(credential); window.location.href = "/logged-in"; } catch (error) { console.log(error); } } passkeyLogin(); </script> </body> </html>
Conditional UI 仅适用于常驻密钥/可发现凭据
建议提供不同的服务器端点以启动 Conditional UI 登录。
客户端需要满足多项要求:
为避免错误,服务器首先应使用以下函数测试客户端可用性:
在真实流量中,检测和生命周期问题通常表现为 NotAllowedError 或 AbortError。使用本指南以区分预期错误与意外错误,包括原生凭据管理器通行密钥错误。
// 来源:https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential/isConditionalMediationAvailable#examples // `window.PublicKeyCredential` 可用意味着 WebAuthn 可用。 if (window.PublicKeyCredential && PublicKeyCredential.isConditionalMediationAvailable) { // 检查条件中介是否可用。 const isCMA = await PublicKeyCredential.isConditionalMediationAvailable(); if (isCMA) { // 调用 WebAuthn 身份验证启动端点 let options = await WebAuthnClient.getPublicKeyRequestOptions(); const credential = await navigator.credentials.get({ publicKey: options.publicKeyCredentialRequestOptions, mediation: "conditional", }); /* ... */ } }
输入字段应接收 HTML 自动填充令牌,该令牌向客户端发出将通行密钥填充到进行中请求的信号。除了通行密钥外,自动填充令牌还可以与现有令牌搭配使用,例如用户名和密码:
<label for="name">用户名:</label> <input type="text" name="name" autocomplete="username webauthn" /> <label for="password">密码:</label> <input type="password" name="password" autocomplete="current-password webauthn" />
WebAuthn 服务器没有强制或标准化的数据库架构。但是,此示例数据库架构可用于存储所需信息并提供 WebAuthn 服务器的所有功能:
加粗属性对于最小可行实现是强制性的,而其他属性仅对于可选但有用的功能才是必需的。
用户显示名称 (user.displayName): 用户友好的、可读的名称,通常是用户的全名。它会显示给用户,但不在身份验证期间使用。
用户名 (user.name): 唯一的、可读的名称,通常是电子邮件地址或用户名。它可以显示给用户,但在身份验证期间不使用。
依赖方 ID (rpID) 是存储在通行密钥中的域,确保通行密钥仅适用于正确的域(浏览器 URL,请参阅本文了解原生应用程序)。在身份验证期间,将针对浏览器 URL 检查 rpID,并且仅在以下两种情况下允许:
以下是(不)允许的组合示例:
以下是用于实施通行密钥的有用工具和网站列表。
chrome://device-log 获得)对于技术实现以外优化通行密钥用户体验的策略,请参阅我们关于通行密钥创建最佳实践和通行密钥登录最佳实践的指南。
如果只想用几行代码在任何应用程序中实施通行密钥,也可以使用 Corbado Complete(用于新应用程序)或 Corbado Connect(用于现有应用程序)
Corbado 是面向大规模运行 consumer 身份验证的 CIAM 团队的Authentication Intelligence Platform。我们让你看到 IDP 日志和通用 analytics 工具看不到的内容:哪些设备、操作系统版本、浏览器和 credential manager 支持 passkey,为什么注册没有转化为登录,WebAuthn 流程在哪里失败,以及什么时候操作系统或浏览器更新会悄悄破坏登录 — 而且无需替换 Okta、Auth0、Ping、Cognito 或你自有的 IDP。两款产品:Corbado Observe 提供 针对 passkey 及任何其他登录方式的 observability。Corbado Connect 提供 内置 analytics 的 managed passkey(与你的 IDP 并存)。VicRoads 通过 Corbado 为 500 万以上用户运行 passkey(passkey 激活率 +80%)。 与 Passkey 专家交谈 →
Conditional UI 需要在启动身份验证前通过 PublicKeyCredential.isConditionalMediationAvailable() 检查浏览器支持。输入字段必须包含 autocomplete="username webauthn" HTML 令牌,并且用户必须已注册常驻密钥(可发现凭据)。建议使用单独的服务器端点来处理 Conditional UI 登录流程。
至少需要存储身份验证器在注册期间生成的凭据 ID,以及用户 ID (user_id),它作为 userHandle 在断言对象中返回。使用凭据 ID 查找关联的用户帐户,并比较 userHandle 以验证身份验证。避免使用 user.name 进行比较,因为它可能会随时间变化。
常驻密钥(可发现凭据)存储在身份验证器本身,并在身份验证期间检索,这是 Conditional UI 正常工作所必需的。非常驻密钥将凭据 ID 存储在服务器上,并在身份验证期间将其发送给身份验证器。authenticatorSelection 中的 residentKey 字段使用“required”、“preferred”或“discouraged”等值来控制此行为。
userVerification 字段控制身份验证器是否必须在登录期间验证用户,接受值为“required”、“preferred”(默认)或“discouraged”。当设置为“preferred”时,用户或其设备可以在身份验证过程中完全跳过验证,这可能会降低安全性。将其设置为“required”可确保始终在身份验证完成之前进行验证。
相关文章
目录