<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>goll (goll)</title>
    <link>https://yubikey.cn/goll</link>
    <description/>
    <language>en-us</language>
    <item>
      <title>FIDO Alliance 公布 Authenticate 2023 的议程</title>
      <description>&lt;h2 id="摘要"&gt;摘要&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;FIDO 联盟宣布了重要讲话嘉宾和完整议程，用于关注用户认证各个方面的独特行业会议 Authenticate 2023。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;今年的主题演讲将由白帽黑客和社会工程专家 Rachel Tobac 发表。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authenticate 2023 将于 10 月 16-18 日在加利福尼亚 La Costa 度假村举行，提供现场和虚拟参会选项。 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;会议现已包括 90 多个由 125 位演讲者主讲的会议，涵盖三个内容轨道。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;赞助商机会也对外开放，企业可以通过品牌曝光、潜在客户产生等各种方式受益。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;8 月 18 日之前可享受提前报名优惠。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="详细信息"&gt;详细信息&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Authenticate 2023 是唯一专注于用户认证各个方面的会议，重点关注基于 FIDO 标准的方法。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;目标吸引 CISO、安全战略家、企业架构师、产品和业务领导者。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;内容包括案例研究、技术教程、专家小组等 90 多个会议。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;主要赞助商包括 1Password、Google、Microsoft 和 Yubico。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;10 月 16-18 日在加利福尼亚 La Costa 度假村现场举行，也提供虚拟参会选项。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;8 月 18 日之前可以享受优惠的提前报名价格。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;有关详情请访问 www.authenticatecon.com，并在 Twitter 上关注&lt;a href="/AuthenticateCon" class="user-mention" title="@AuthenticateCon"&gt;&lt;i&gt;@&lt;/i&gt;AuthenticateCon&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;来自：&lt;a href="https://fidoalliance.org/fido-alliance-details-agenda-for-authenticate-2023-featuring-keynote-from-rachel-tobac-noted-white-hat-hacker-socialproof-security-ceo/" rel="nofollow" target="_blank"&gt;https://fidoalliance.org/fido-alliance-details-agenda-for-authenticate-2023-featuring-keynote-from-rachel-tobac-noted-white-hat-hacker-socialproof-security-ceo/&lt;/a&gt;&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Fri, 04 Aug 2023 09:04:19 +0800</pubDate>
      <link>https://yubikey.cn/topics/34</link>
      <guid>https://yubikey.cn/topics/34</guid>
    </item>
    <item>
      <title>为什么说密码的时间不多了？</title>
      <description>&lt;h2 id="密码的问题"&gt;密码的问题&lt;/h2&gt;
&lt;p&gt;尽管自文字出现伊始就被广泛使用，作为验证我们可以信任的人的方式，但 passwords 显然不是实现这一目标的好方法。&lt;/p&gt;

&lt;p&gt;passwords 容易在多方面受到攻击。它们可以被盗取、遗失、泄露，可以被攻击者重置，此外，它们也可能被猜测或遗忘 - 后两者正是某些非常可预测的人为行为所导致的结果。&lt;/p&gt;
&lt;h2 id="认证的三大要素"&gt;认证的三大要素&lt;/h2&gt;
&lt;p&gt;在认证 (即证明你是你所说的那个人) 方面，任何人或任何系统通常会要求你提供以下三大要素：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;你知道的 (如密码或 PIN 码)- 通常是最弱的认证方式&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;你拥有的 (如智能手机)- 更难被获取&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;你是什么 (如生物特征)- 同样难以复制&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="密码的可用性差"&gt;密码的可用性差&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://cdn.yubikey.cn/photo/goll/fc60a9b9-663d-4b63-97ab-eb95796e508d.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;“你知道的”这种方式需要记忆力。记住所有密码非常困难，各种密码要求也没有让情况变得更好：必须超过 n 个字符、包含大小写字母、数字和特殊字符等。这导致人们重复使用密码、设置简单密码，并最终忘记密码。&lt;/p&gt;

&lt;p&gt;相比之下，“你拥有的”和“你是什么”这两种方式相对更易用。&lt;/p&gt;
&lt;h2 id="Passkeys 解决方案"&gt;Passkeys 解决方案&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://cdn.yubikey.cn/photo/goll/10bb5b86-5e31-4280-be0c-66c79b948dcf.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Passkeys 并非是一个全新的概念 - 它们是一种存在几十年的认证方式的重新发明：公钥加密。&lt;/p&gt;

&lt;p&gt;每个 Passkey 由两部分组成：公钥和私钥。&lt;/p&gt;

&lt;p&gt;公钥对所有人开放，但它只是证明身份所需的一半。私钥存储在你的设备上，不应向任何人透露。&lt;/p&gt;

&lt;p&gt;这两个密钥用于以下流程中证明你的身份：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;尝试登录网页或应用时，你的设备发送公钥，唯一标识你是试图登录的用户。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;然后，网站会向你的设备发送一个挑战。该挑战被加密，只有你的私钥才能解密。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;如果你的私钥可以解密挑战，就证明了你是你所声称的那个人。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这种方法的巧妙之处在于私钥不会明文传输，因此它不会被强取或被黑客截获。 &lt;/p&gt;
&lt;h2 id="如何开始使用 Passkeys"&gt;如何开始使用 Passkeys&lt;/h2&gt;
&lt;p&gt;当前有一些 Passkey 提供商，包括谷歌、苹果、Bitwarden 等。一些主要网站如谷歌、TikTok、GitHub 也已经开始提供它作为一种认证选项。&lt;/p&gt;

&lt;p&gt;Passkeys 可能需要一段时间才能被广泛采用，但它代表了登录认证技术的重大进步，预计在未来几年内将逐步取代传统的密码系统。
来自：&lt;a href="https://medium.com/@supersimplecyber/passwords-days-are-numbered-here-s-why-e193ee5bd2a9" rel="nofollow" target="_blank"&gt;https://medium.com/@supersimplecyber/passwords-days-are-numbered-here-s-why-e193ee5bd2a9&lt;/a&gt;&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Mon, 24 Jul 2023 17:42:04 +0800</pubDate>
      <link>https://yubikey.cn/topics/32</link>
      <guid>https://yubikey.cn/topics/32</guid>
    </item>
    <item>
      <title>Passkey 即将取代 Password,这意味着什么?</title>
      <description>&lt;h2 id="Passkey即将取代Password,这意味着什么?"&gt;Passkey 即将取代 Password，这意味着什么？&lt;/h2&gt;
&lt;p&gt;随着各大技术公司宣布在未来几年内逐步取消密码 (Password),代之以更安全的登录方式 Passkey，传统的以密码为基础的账号安全体系也将面临革新。&lt;/p&gt;
&lt;h2 id="什么是Passkey?"&gt;什么是 Passkey?&lt;/h2&gt;
&lt;p&gt;Passkey 是一种基于公钥密码学的新型登录方式。 &lt;/p&gt;

&lt;p&gt;它不需要你设置和记忆复杂的密码，取而代之的是将你的设备或生物特征与网站或应用关联，让你的设备或生物特征成为打开账号的钥匙。&lt;/p&gt;

&lt;p&gt;举例来说，你可以用手机或者 Windows Hello 脸部识别来登录网站或应用。登录时不需要输入密码，系统会自动识别已关联的设备或生物特征，验证通过后即可进入。&lt;/p&gt;

&lt;p&gt;整个过程不会在设备与服务器之间传输或存储任何秘密，更安全也更便捷。&lt;/p&gt;
&lt;h2 id="Passkey与Password相比有哪些优势?"&gt;Passkey 与 Password 相比有哪些优势？&lt;/h2&gt;
&lt;p&gt;与基于密码的传统方式相比，Passkey 具有以下优势：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;更安全:Passkey 基于非对称密钥加密，秘密不会在网络上传输或存储，防范数据泄漏和社会工程学风险。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;更便捷：不需要设置和记忆复杂密码，登录可以完全依赖已关联的设备或生物特征。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;无需密码管理器：用户不需要再依赖密码管理器生成和存储各种不同的密码。 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;多设备同步:Passkey 系统可以在多设备间无缝同步，一个 Passkey 可以在手机、电脑等设备上通用。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;无需短信或邮件验证：基于公钥密码学，不需要通过短信或邮件重置密码。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;更好的用户体验：无需频繁设置新密码，不会出现忘记密码的问题。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="主要技术公司的部署计划"&gt;主要技术公司的部署计划&lt;/h2&gt;
&lt;p&gt;包括 Apple、Google、Microsoft 在内的多家科技巨头已经宣布了在未来几年内全面推进 Passkey 的部署计划：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Apple:iOS 16 和 MacOS Ventura 将支持 Passkey，可在 Apple 设备间无缝同步。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Google:Chrome 和 Android 已支持 WebAuthn Passkey 标准，会在更多 Google 服务中推广 Passkey。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Microsoft:Windows、Edge 和 Azure 已支持 WebAuthn，未来版本的 Windows 也将内置对 Passkey 的支持。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FIDO 联盟：许多公司参与了 FIDO 联盟，共同推进无密码认证标准。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Passkey普及还面临哪些挑战?"&gt;Passkey 普及还面临哪些挑战？&lt;/h2&gt;
&lt;p&gt;尽管大厂纷纷宣布部署 Passkey，但从 Password 完全过渡到 Passkey 仍有诸多挑战：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;需要设备厂商和网站站点的广泛支持，需要时间逐步推进。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;用户需要适应以新的方式管理登录，并在不同设备间同步 Passkey。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;需要解决手机丢失后如何恢复账号访问的问题。 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;虽更安全，但 Passkey 也需要防范钓鱼和其他新的攻击手段。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;部分用户群体可能暂时无法采用只支持新设备的 Passkey 系统。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;Passkey 代表了登录认证技术的重大进步，有望在未来几年内逐步取代传统的密码系统，带来更安全便捷的用户体验。但从 Password 完全迁移到 Passkey 还需要技术生态的共同努力和用户习惯的适应。&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Wed, 19 Jul 2023 09:41:06 +0800</pubDate>
      <link>https://yubikey.cn/topics/30</link>
      <guid>https://yubikey.cn/topics/30</guid>
    </item>
    <item>
      <title>什么是 Passkey？有什么大不了的？</title>
      <description>&lt;p&gt;&lt;img src="https://cdn.yubikey.cn/photo/goll/d602adda-574c-4f79-b10d-3b2df6a81b77.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;你的 Netflix 密码是什么？那么 Spotify 的是什么？很好...... Facebook 的又是什么？现在希望你不是把它们大声念出来，但可能性是，你要么回忆起了类似或相同的密码，要么因为它们非常复杂而在你的密码管理器里 (无意冒犯，做得好) 而没有回答我的问题。&lt;/p&gt;

&lt;p&gt;我不需要成为第一个说密码是我们日常生活的一部分的人。我们不断地使用它们登录我们的账户，创建新账户时创建它们，因为我们忘记了密码而重置它们，被告知你不能用最近使用的密码重置你的密码...... 此列举始终如一。这显然导致了与重用密码和我们存储它们的安全性有关的问题。&lt;/p&gt;

&lt;p&gt;啊，密码管理器！一个很好的方式来管理我们可能意识到或可能没有意识到我们拥有的数百个账户，同时保持心理平静和安全感，拥有独特而且生成复杂的密码。然而，最近的 LastPass 违规行为是一个重大的警钟，并证明我们需要警惕密码存储和保护的方式，即使是在广泛流行的密码管理器上。&lt;/p&gt;

&lt;p&gt;Passkeys 现在正在成为注册和登录的一种方式，它们是 FIDO 对无密码未来的最新实现。使用公钥密码术，每个 passkey 都是一对公钥和私钥的密码对，并在您注册支持它的网站时生成。顾名思义，公钥与您注册的服务共享，并存储在其服务器上。私钥留在您的设备上。当您尝试登录时，您正在登录的服务器会将一个挑战发回您的设备。您指定的私钥将解决该挑战，对其进行签名，从而验证您是...您！服务器可以使用您的公钥进行验证，但不需要知道您的私钥。它们也在您的设备之间同步 (比如苹果的 iCloud 实现),以便如果您丢失或损坏了某个设备，您仍然可以访问您的账户。&lt;/p&gt;
&lt;h2 id="为什么我们要关心?"&gt;为什么我们要关心？&lt;/h2&gt;
&lt;p&gt;与密码管理器的目标类似，Passkeys 消除了钓鱼、社会工程和由于复杂性不足而猜测密码的问题。在公司遭受数据泄露的情况下，您的公钥可能会泄露...但是您的账户仍然安全！只要您的私钥与您在一起，公钥对攻击者来说就没有价值，就像没有密码的用户名一样 (但这次您的密码不是“qwerty123”,随后被暴力破解)&lt;/p&gt;

&lt;p&gt;如果我们看一下一个流行的密码管理器 Dashlane，他们保证“零知识加密”。根据他们的安全原则和架构：&lt;/p&gt;

&lt;p&gt;“访问保险库需要用户主密码，该密码仅为帐户持有人所知。此密码未存储在 Dashlane 的服务器上，Dashlane 员工无法访问。Dashlane 使用单独的用户设备密钥在其服务器上对每个人进行身份验证。”&lt;/p&gt;

&lt;p&gt;这听起来很像我描述的... 因为它们都使用公钥密码术。Passkeys 与“主密码”的区别在于“主密码”不是传统密码，而是您的生物识别信息，允许您的私钥被使用。那么，我们为什么要关心呢？&lt;/p&gt;

&lt;p&gt;目前，安全登录账户的方法是使用密码和第二个认证因素。这可以是 OTP(一次性密码，如短信验证码)、物理安全密钥或身份验证器应用程序。这可确保登录需要您拥有的内容 (密码) 和拥有的内容 (安全密钥)。&lt;/p&gt;

&lt;p&gt;Passkey 在一次步骤中满足这些要求，这简化了用户的登录体验，同时保持高度安全性。您不必查找密码，然后检查手机的身份验证器应用程序获取二次代码，您可以简单地使用便捷的指纹 (无双关意味),或您的设备支持的任何其他生物识别因素，就像魔法一样... 您已经设置了一种安全的身份验证形式，可以开始使用了。这不是您的密码的第二个因素，而是您的密码的替代品。&lt;/p&gt;

&lt;p&gt;由于每个 Passkey 与特定站点相关联，所以它被称为抗钓鱼，因为您的 Passkey 只能在注册的站点上工作，减少了被欺骗登录到复制站点以窃取您的凭据的担忧。&lt;/p&gt;

&lt;p&gt;所以.. Passkey 很棒，因为它们 (和我一起说)..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;抗钓鱼&lt;/li&gt;
&lt;li&gt;在数据泄露的情况下也能保护您的账户安全&lt;/li&gt;
&lt;li&gt;简化注册和登录流程，同时保持高安全级别&lt;/li&gt;
&lt;li&gt;无法被猜测&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Passkey 还非常新的，支持它的用户还很少，但在写这篇文章时，GitHub 已经宣布支持！&lt;/p&gt;

&lt;p&gt;我非常期待看到 Passkey 的采用，因为它似乎是一个更简单、更安全的登录方式。&lt;/p&gt;

&lt;p&gt;如果您还没有，请自己试一试！我在下面提供了一个链接来测试生成和使用 passkey 的过程：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.passkeys.io/" rel="nofollow" target="_blank"&gt;https://www.passkeys.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;总而言之，密码往往被重复使用、钓鱼和在数据泄露的事件中脆弱，我们知道这一点。MFA(多因素身份验证) 有助于防止钓鱼，并通过与您的密码一起使用辅助因素来确保登录得到验证。然而，Passkeys 通过为我们节省一步，并为您生成一个安全的非对称密钥对，将此推向了一个新的高度。因此，它允许您使用生物识别信息登录，确保是您本人，并在服务器端遭受泄露的情况下保护您的账户安全。使用起来比听起来简单得多，我鼓励您在支持的网站或上面我提供的演示链接上尝试一下。&lt;/p&gt;

&lt;p&gt;您对 Passkeys 有何看法？请在评论中告诉我，我很想听听您的想法！&lt;/p&gt;

&lt;p&gt;来自：&lt;a href="https://medium.com/@tokyoishy/passkeys-whats-the-big-deal-618be83c17f4" rel="nofollow" target="_blank"&gt;https://medium.com/@tokyoishy/passkeys-whats-the-big-deal-618be83c17f4&lt;/a&gt;&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Mon, 17 Jul 2023 14:12:22 +0800</pubDate>
      <link>https://yubikey.cn/topics/28</link>
      <guid>https://yubikey.cn/topics/28</guid>
    </item>
    <item>
      <title>开发者入门指南：基础概念 - 网络应用身份验证</title>
      <description>&lt;h2 id="开发者须知:网页应用认证的基础概念"&gt;开发者须知：网页应用认证的基础概念&lt;/h2&gt;&lt;h2 id="认证是网络安全的一个关键和重要方面,它验证用户的身份。它涉及在授予他们访问特定资源或服务之前,验证个人或实体的身份。在网络认证中,使用了各种方法和技术,包括无密码和联合凭证管理API(FedCM)。"&gt;认证是网络安全的一个关键和重要方面，它验证用户的身份。它涉及在授予他们访问特定资源或服务之前，验证个人或实体的身份。在网络认证中，使用了各种方法和技术，包括无密码和联合凭证管理 API(FedCM)。&lt;/h2&gt;
&lt;p&gt;认证不应与授权混淆，这是网络应用安全领域中的两个不同概念。以下是两者的区别：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;认证&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;认证是验证或确定访问网络应用的用户或实体身份的过程。&lt;/p&gt;

&lt;p&gt;它确保用户在被授予访问系统或特定资源之前，是他们所声称的身份。 &lt;/p&gt;

&lt;p&gt;认证通常在会话开始时或访问应用程序受限区域时执行。&lt;/p&gt;

&lt;p&gt;常见的认证方法或策略包括用户名和密码、生物特征认证、双因素认证 (2FA) 和证书等。&lt;/p&gt;

&lt;p&gt;其主要目标是建立信任，并确保只有授权的个人或实体可以访问该应用程序。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;授权&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;授权是向经过身份验证的用户或实体授予或拒绝访问权限和权限的过程。&lt;/p&gt;

&lt;p&gt;一旦通过认证建立了用户的身份，授权就决定他们被允许在应用程序内访问哪些操作或资源。&lt;/p&gt;

&lt;p&gt;授权基于预定义的规则、策略或与用户角色或特定属性相关的权限。&lt;/p&gt;

&lt;p&gt;它涉及定义和实施访问控制来保护敏感数据和功能。&lt;/p&gt;

&lt;p&gt;授权机制可以包括基于角色的访问控制 (RBAC)、基于属性的访问控制 (ABAC) 和其他细粒度的权限模型。&lt;/p&gt;

&lt;p&gt;授权的主要目标是确保经过身份验证的用户具有适当的特权，以在应用程序内执行他们预期的操作，同时防止未经授权的访问。&lt;/p&gt;

&lt;p&gt;认证和授权都是网络应用安全的关键组成部分，它们共同保护数据、维护系统完整性并防止未经授权的访问或滥用。&lt;/p&gt;
&lt;h2 id="现在我们明白认证用于识别,而授权用于控制对资源和功能的访问。"&gt;现在我们明白认证用于识别，而授权用于控制对资源和功能的访问。&lt;/h2&gt;&lt;h3 id="认证策略:"&gt;认证策略：&lt;/h3&gt;
&lt;p&gt;这里是你可能遇到的最常见的认证策略：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;密码 (用户名/密码):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;通常称为用户名和密码的密码，已经被广泛用于认证。用户在登录过程中提供他们的用户名和密码。服务器将输入的凭据与存储的与该用户名相关联的凭据进行比较。如果凭据匹配，则授予用户访问权限。但是，密码具有弱密码、密码重用和基于密码的攻击等漏洞，这会危及安全性。&lt;/p&gt;

&lt;p&gt;这种策略需要大量的人工管理，如果处理不当，后果可能会很严重。一个错误方法的直接指标是例如：不使用安全协议如 https，在数据库中以明文存储密码而不进行哈希或加盐等。&lt;/p&gt;

&lt;p&gt;暴力破解攻击、密码猜测和凭据填充是常见的认证漏洞。暴力破解攻击是指有系统地尝试所有可能的组合来猜测密码。密码猜测是指根据个人信息、社会工程或钓鱼攻击尝试猜测用户的密码。凭据填充是指从一个站点自动注入到另一个站点的被盗用户名和密码。实施强密码策略、账户锁定、双因素认证 (2FA)、代码生成器、一次性密码 (OTP) 和 CAPTCHA 等机制可以减轻这些漏洞。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;单点登录 (SSO) 和联合身份认证：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;联合身份认证 (FedCM) 是一种使用户能够使用一组凭证登录多个网络服务或应用程序的方法。它减少了用户为不同服务记忆多个用户名和密码的需要。用户向身份提供者 (IdP) 进行身份验证，例如 Twitter、Facebook、Google，后者会发出身份验证令牌或断言。然后，各种网络服务可以使用该令牌来验证用户的身份，而无需直接管理用户凭据。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://cdn.yubikey.cn/photo/goll/6c850545-0a6f-4b33-bfd8-592961314536.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在实施 FedCM 时，网络用户在认证过程中会被网络服务重定向到 IdP。IdP 会对用户进行身份验证并发出身份验证令牌或断言。然后，网络服务会验证令牌的有效性，其中包括检查其到期时间、签名和其他安全措施。SAML(安全断言标记语言) 和 OIDC(OpenID Connect) 等标准化协议有助于 IdP 和网络服务之间的安全通信。&lt;/p&gt;

&lt;p&gt;FedCM 提供了改善用户体验、减少密码疲劳和集中管理用户凭据等优势。它简化了身份验证过程，通过与可信 IdP 集中身份验证管理来增强安全性，并启用跨多个应用程序的单点登录 (SSO)。&lt;/p&gt;

&lt;p&gt;单点登录允许用户进行一次身份验证，然后无需重新输入凭据即可访问多个应用程序或服务。SSO 通过减少用户记忆和管理多个凭据的需要来简化身份验证过程、改善用户体验并提高生产力。它还可以帮助组织实施一致的安全策略和集中化的用户管理。&lt;/p&gt;

&lt;p&gt;SSO 有各种实现，包括基于 OpenID Connect (OIDC) 的 SSO 和 SAML 2.0。OIDC 是 OAuth 2.0 之上的身份层，使用基于 JSON 的令牌 (例如 JWT 令牌) 提供了一种现代灵活的 SSO 方法。SSO 通常依靠会话管理技术在不同的应用程序之间维护用户身份验证。常用的方法包括使用安全会话令牌、Cookie 或持久身份验证令牌。&lt;/p&gt;

&lt;p&gt;SSO 集成需要 IdP 和 SP(服务提供者) 或你的网络服务之间的协调，包括建立信任、交换元数据以及实现必要的协议和标准。Azure Active Directory、Okta 或 Keycloak 等联合身份提供商为组织提供 SSO 身份和访问管理解决方案。&lt;/p&gt;

&lt;p&gt;为了在多个域或应用程序之间实现 SSO，在身份提供者 (IdP) 和服务提供者 (SP) 之间交换身份验证和授权数据时，使用 SAML。IdP 认证用户并发出包含用户身份信息的 SAML 断言。SP 根据 IdP 提供的断言授予对其资源或服务的访问权限。SAML 断言包含有关用户身份、属性和认证状态的信息。&lt;/p&gt;

&lt;p&gt;SSO 和 FedCM 简化了用户的身份验证过程，通过减少记忆和管理多个凭据的需要来提高用户体验，并通过消除重复登录来提高生产力。它还可以帮助组织实施一致的安全策略和集中化的用户管理。&lt;/p&gt;

&lt;p&gt;需要考虑的因素包括仔细选择可靠的 IdP、确保安全的令牌处理以及在认证之后正确管理授权。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;代码交换的证明密钥 (PKCE)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PKCE 是 OAuth 2.0 授权代码流的一个安全扩展，可以防止授权代码拦截攻击。&lt;/p&gt;

&lt;p&gt;OAuth 2.0 的授权代码流涉及交换授权代码以获取访问令牌。这要求客户端将客户端 ID 和客户端机密发送到授权服务器以获取访问令牌。由于网络、本地和移动应用程序在安全存储和交换客户端机密方面面临安全挑战，这本身容易受到“授权代码拦截攻击”。PKCE 的引入是为了解决这个问题，方法是在授权代码流中添加一个额外的安全层。&lt;/p&gt;

&lt;p&gt;使用 PKCE 时，客户端不发送客户端机密，而是生成一个随机的代码验证器并将其转换为代码质询。代码质询与授权请求一起发送到授权服务器。服务器将代码质询与生成的授权代码相关联。在交换授权代码以获取访问令牌时，客户端发送原始代码验证器，以证明其拥有该机密代码。&lt;/p&gt;

&lt;p&gt;PKCE 帮助防止授权代码在传输中被拦截。即使授权代码被拦截，也只有启动授权流程的客户端才能获得访问令牌。&lt;/p&gt;
&lt;h3 id="附加网络应用安全注意事项"&gt;附加网络应用安全注意事项&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;跨站点脚本 (XSS) 和跨站点请求伪造 (CSRF):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;XSS 是一种漏洞，允许攻击者向其他用户查看的网页中注入恶意脚本。当应用程序在将用户提供的输入呈现为 HTML 响应的一部分之前没有适当地验证或过滤时，就会发生这种情况。&lt;/p&gt;

&lt;p&gt;CSRF 是一种漏洞，允许攻击者欺骗经过身份验证的用户在网站或 Web 应用程序上执行不需要的操作。当应用程序没有充分验证请求的源时，就会发生这种情况，从而允许恶意行为者伪造请求代表毫不知情的用户。&lt;/p&gt;

&lt;p&gt;讨论防范措施，比如输入验证、输出编码和反 CSRF 令牌，以减轻这些漏洞。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;安全头：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;安全头在通过提供针对各种攻击的额外保护层来增强 Web 应用程序的安全方面发挥着关键作用。示例包括内容安全策略 (CSP)、严格传输安全 (HSTS)、X-Content-Type-Options、Referrer-Policy 和 X-Frame-Options。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;安全会话管理：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;实施安全的会话管理实践有助于保护用户帐户、敏感数据，并确保用户会话不会遭到破坏。必须遵循安全编码实践，利用安全会话存储机制，包括会话过期、安全会话存储和会话固定预防。&lt;/p&gt;

&lt;p&gt;Web 安全是一个需要持续学习的广泛主题。这里讨论的实现只是对常用策略的高级概述。我鼓励您研究并学习有关此主题的更多信息。&lt;/p&gt;
&lt;h3 id="额外参考资料"&gt;额外参考资料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/securing/what-the-heck-is-pkce-d75e57e74cf8" rel="nofollow" target="_blank" title=""&gt;什么是 PKCE?&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@alexmigwi/useful-concepts-to-understand-when-using-keycloak-9fe501db87e8" rel="nofollow" target="_blank" title=""&gt;使用 Keycloak 时需要了解的有用概念&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.postman.com/oauth-2-0-implicit-flow-is-dead-long-live-pkce/" rel="nofollow" target="_blank" title=""&gt;Postman 博客 - OAuth 2.0:隐式流已死，请尝试 PKCE&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;更多网络安全信息&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;来自：&lt;a href="https://alex-migwi.medium.com/developer-101-foundational-concepts-web-apps-authentication-157d76220948" rel="nofollow" target="_blank"&gt;https://alex-migwi.medium.com/developer-101-foundational-concepts-web-apps-authentication-157d76220948&lt;/a&gt;&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Thu, 13 Jul 2023 09:26:15 +0800</pubDate>
      <link>https://yubikey.cn/topics/24</link>
      <guid>https://yubikey.cn/topics/24</guid>
    </item>
    <item>
      <title>在 Next.JS 上实施 WebAuthn（Passkeys）</title>
      <description>&lt;h2 id="Implementing Apple Passkeys using Next.JS and MongoDB"&gt;Implementing Apple Passkeys using Next.JS and MongoDB&lt;/h2&gt;
&lt;p&gt;在 WWDC 2022 中，我们看到 Apple 展示了“Passkeys”，这是一种彻底改变了无密码身份验证的全新身份验证流程。这个新的协议被称为 webauthn。在本文中，我将在 NextJS 和 mongoose（mongoDB）中实现这项新技术。&lt;/p&gt;
&lt;h2 id="项目的起步："&gt;项目的起步：&lt;/h2&gt;
&lt;p&gt;首先，让我们从创建一个新的 NextJS 项目开始，运行&lt;code&gt;npx create-next-app&lt;/code&gt;并回答问题。我将把项目称为&lt;code&gt;learningwebauthn&lt;/code&gt;，你可以自由地给它任何你喜欢的名字。我在这个项目中不使用 TypeScript。如果你打算使用 TypeScript，你需要自行进行必要的更改。我使用 TailwindCSS 进行样式。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="安装依赖项："&gt;安装依赖项：&lt;/h2&gt;
&lt;p&gt;进行依赖项安装之前，记得先切换到项目的目录。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;learningwebauthn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们现在将安装项目的依赖项，在之前提到的，我们将使用 mongoose 作为数据库。由于 NextJS 不支持 session，我们还将使用 iron-session 进行会话管理。我们还将使用&lt;a href="/github" class="user-mention" title="@github"&gt;&lt;i&gt;@&lt;/i&gt;github&lt;/a&gt;/webauthn-json 来实现 webauthn。运行以下命令来下载和安装这些包。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;mongoose iron-session @github/webauthn-json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们还需要一个包——&lt;a href="/simplewebauthn" class="user-mention" title="@simplewebauthn"&gt;&lt;i&gt;@&lt;/i&gt;simplewebauthn&lt;/a&gt;/server，我们将用其进行挑战验证。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @simplewebauthn/server
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="设置数据库："&gt;设置数据库：&lt;/h2&gt;
&lt;p&gt;让我们开始设置 mongoose。&lt;/p&gt;

&lt;p&gt;在项目的根目录中创建一个名为 lib 的新文件夹。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;lib
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 lib 文件夹中创建一个名为 dbConnect.js 的文件。该文件将包含连接到 MongoDB 的所有必要代码。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MONGODB_URI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGODB_URI&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please define the MONGODB_URI environment variable inside .env.local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mongoose&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dbConnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bufferCommands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;dbConnect&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码中，我们引入了 mongoose，然后我们检查存储在 MONGODB_URI 环境变量中的 MongoDB URI。然后，我们定义了一个名为 cached 的新对象，该对象尝试从全局变量中获取 mongoose 实例。在下一行中，如果实例不存在于全局变量中，我们创建一个对象，该对象表示连接和承诺都为 null。现在我们定义一个函数，该函数返回数据库连接。如果连接已经存在于缓存中，我们只需返回相同的连接。如果未定义承诺，则运行 mongoose.connect(...) 以使用 URI 建立与数据库的连接，在获取到连接后，将其保存在缓存中。之后，我们尝试等待承诺以获取连接。如果发生错误，我们抛出错误。之后，连接可在 cached.conn 处使用，我们返回它。最后一行只是默认导出该函数。&lt;/p&gt;
&lt;h2 id="定义数据模型："&gt;定义数据模型：&lt;/h2&gt;
&lt;p&gt;现在让我们定义用于存储用户和用户信息的模型。&lt;/p&gt;

&lt;p&gt;在 models 中创建一个名为 models 的新文件夹。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;models
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在该文件夹中，创建一个名为 User.js 的新文件。此文件将包含用户模型的代码，你可以根据你的项目存储其他用户信息。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在同一个文件夹中，创建一个名为 Credential.js 的新文件。此文件将包含与用户关联的凭据模型。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CredentialsSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;externalId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;dateAdded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Credentials&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;  &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CredentialsSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="会话设置："&gt;会话设置：&lt;/h2&gt;
&lt;p&gt;接下来设置会话 cookie。&lt;/p&gt;

&lt;p&gt;在 lib 文件夹中创建一个名为 session.js 的文件，在文件中添加以下代码：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withIronSessionApiRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withIronSessionSsr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;iron-session/next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessionOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cookieName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webauthn-token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COOKIE_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cookieOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withSessionAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;withIronSessionApiRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;withIronSessionSsr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码导出了一个具有会话选项的对象，它具有一个存储在名为 COOKIE_SECRET 的环境变量中的密码，这可以确保 cookie 的安全存储。cookieName 用于在浏览器上识别 cookie。选项设置为在生产环境下加密 cookie。我们还导出了两个函数，我们稍后将在后面的代码中使用它们来获取会话。&lt;/p&gt;
&lt;h2 id="注册页面："&gt;注册页面：&lt;/h2&gt;
&lt;p&gt;现在让我们开始编写实际面向用户的页面，在注册页面中，我们将接受用户的信息并设置 passkey。&lt;/p&gt;

&lt;p&gt;在 pages 中创建一个名为 register.js 的文件，并添加以下代码：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;supported&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@github/webauthn-json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;support&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSupport&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkAvailability&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;available&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PublicKeyCredential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isUserVerifyingPlatformAuthenticatorAvailable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setSupport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;supported&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;checkAvailability&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleRegister&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex min-h-full flex-col justify-center px-6 py-12 lg:px-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mt-10 text-center text-2xl font-bold leading-9 tracking-tight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;Register&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mt-10 sm:mx-auto sm:w-full sm:max-w-sm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;support&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/register&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleRegister&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;
                &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block text-sm font-medium leading-6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;Email&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
                &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block w-full rounded-md border-0 py-1.5 text-black&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
              &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;
                &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block text-sm font-medium leading-6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;Username&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
                &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block w-full rounded-md border-0 py-1.5 text-black&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
              &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mt-10 sm:mx-auto sm:w-full sm:max-w-sm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
                &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;w-full bg-blue-600 p-3 rounded-md py-1.5 block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;Register&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Sorry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="nx"&gt;does&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;support&lt;/span&gt; &lt;span class="nx"&gt;WebAuthn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上代码是一个 React 元素，它定义了注册页面和两个字段（电子邮件和用户名）。只有在用户的浏览器支持 webauthn 时，表单才会渲染。表单将其提交到 API，API 将返回相关信息以继续该过程。表单的 onSubmit 事件链接到后面将要编写的函数。在这里，我们需要处理用户信息。到目前为止，该项目应该是可运行的。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行上面的命令，然后使用浏览器打开&lt;a href="http://localhost:3000/register" rel="nofollow" target="_blank"&gt;http://localhost:3000/register&lt;/a&gt;。这里你应该会看到我们设计的页面。&lt;/p&gt;
&lt;h2 id="生成挑战："&gt;生成挑战：&lt;/h2&gt;
&lt;p&gt;根据 webauthn 协议，我们需要生成一个挑战来实现 webauthn。在 lib/auth.js 文件中添加新代码。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\+&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/=/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateChallenge&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码包含两个函数，clean 函数用于删除可能破坏身份验证过程的符号，第二个函数 generateChallenge 用于生成实际的挑战，并返回适用于身份验证过程的清理版本的挑战。&lt;/p&gt;
&lt;h2 id="运行挑战："&gt;运行挑战：&lt;/h2&gt;
&lt;p&gt;现在我们回到 pages/register.js 并添加 getServerSideProps 函数以及会话代码。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;supported&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@github/webauthn-json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;generateChallenge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Same as before&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateChallenge&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码允许在服务器端呈现页面，我们生成挑战并使其在元素中可用。我们还将其保存在使用 iron-session 库的会话中。&lt;/p&gt;

&lt;p&gt;来自：&lt;a href="https://medium.com/@vachanmn123/implementing-webauthn-passkeys-on-next-js-baa36ecd59cb" rel="nofollow" target="_blank"&gt;https://medium.com/@vachanmn123/implementing-webauthn-passkeys-on-next-js-baa36ecd59cb&lt;/a&gt;&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Tue, 11 Jul 2023 09:08:00 +0800</pubDate>
      <link>https://yubikey.cn/topics/23</link>
      <guid>https://yubikey.cn/topics/23</guid>
    </item>
    <item>
      <title>2023 年 FIDO 亚太峰会将在越南举行</title>
      <description>&lt;p&gt;该活动将聚集该地区的行业领导者、网络安全专家和政府代表，探讨身份验证技术的最新发展。&lt;/p&gt;

&lt;p&gt;2023 年 6 月 26 日，新加坡——FIDO 联盟宣布举办首次 FIDO APAC 峰会 2023，这是专门促进和推动防钓鱼 FIDO 认证的区域性顶级活动。这次峰会由越南信息和通信部共同主办，将于 2023 年 8 月 28 日至 30 日在越南芽庄的 Vinpearl 举行。&lt;/p&gt;

&lt;p&gt;亚太地区的网络安全格局在近年来经历了显著的增长和变革，这是由于快速的数字化、增加的互联网普及率以及云计算、人工智能和物联网等先进技术的迅速采用推动的。随着企业和政府对数字基础设施的依赖程度越来越高，网络威胁变得越来越复杂和普遍，导致了突出的网络攻击和数据泄露的激增。2012 年全球的所有事件中，亚太地区占据了 31%，因此，现在组织采取必要的措施来使用更强大的身份验证方法至关重要。&lt;/p&gt;

&lt;p&gt;今年的峰会主题是“为更安全的数字未来连接”，旨在突出安全的防钓鱼身份验证方法，专注于 FIDO 标准和密码。峰会将汇集来自该地区的各行业领导者、网络安全专家和政府代表，讨论最新发展并分享最佳实践和成功案例。参会者可以期待有深入的主题演讲、引人入胜的座谈会讨论、全面的技术研讨会和充裕的网络交流机会。&lt;/p&gt;

&lt;p&gt;FIDO 联盟的执行主任安德鲁·斯基亚尔表示：“FIDO 联盟很高兴在越南举办首届亚太峰会 2023。我们在全球范围内目睹着越来越多的来自弱密码或被盗凭据的网络攻击和诈骗事件，亚太地区也不例外。幸运的是，这里的组织已经稳步采用基于防钓鱼 FIDO 认证的密码，以应对这些威胁。通过这次峰会，我们希望促进在身份验证的各个领域进行知识共享，并鼓励任何有兴趣了解更多的人加入我们。”&lt;/p&gt;

&lt;p&gt;越南信息和通信部副部长阮惠东表示：“我们很高兴参与组织这次活动。”他强调：“我们完全支持采用无密码认证技术来保护越南的数字经济。”他继续说道：“我们的愿望是与 FIDO 联盟和亚太地区的其他国家建立联系和合作，共同为更安全的数字未来努力。”&lt;/p&gt;

&lt;p&gt;此次会议将有来自亚太地区的 25 多位特邀嘉宾和演讲者参加，预计将有超过 300 名参会者。今年的主要峰会演讲人包括来自 FIDO 联盟成员公司的 VinCSS、Google、Mastercard、三星电子、NTT DOCOMO、SK 电讯、SecureMetric、AirCuve、ETDA 和 MK Group 等。&lt;/p&gt;

&lt;p&gt;现在向公众开放注册。尽管活动免费，但所有代表都需要在峰会场地 Vinpearl Resort Nha Trang 预订至少三晚的住宿。&lt;/p&gt;

&lt;p&gt;关于 FIDO 联盟&lt;/p&gt;

&lt;p&gt;FIDO（Fast IDentity Online）联盟成立于 2012 年 7 月，旨在解决强身份验证技术之间的互操作性问题，解决用户面临的创建和记住多个用户名和密码的问题。FIDO 联盟通过简化、更强大的认证标准改变了认证的性质，定义了一组开放、可扩展、互操作的机制，减少对密码的依赖。与在线服务进行认证时，FIDO 认证更强大、隐私性更好、更易于使用。&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Fri, 07 Jul 2023 17:09:16 +0800</pubDate>
      <link>https://yubikey.cn/topics/21</link>
      <guid>https://yubikey.cn/topics/21</guid>
    </item>
    <item>
      <title>ITU 将 FIDO Alliance 的更新技术规范作为国际标准采纳</title>
      <description>&lt;p&gt;2023 年 6 月 16 日，FIDO 联盟宣布其规范之一 FIDO UAF 1.2 和 CTAP 2.1 被国际电信联盟电信标准化部门（ITU-T）认定为国际标准。这里里程碑使这些标准成为全球信息和通信技术基础设施的官方 ITU 标准（ITU-T 建议）。&lt;/p&gt;

&lt;p&gt;ITU-T 是 ITU 的标准化部门，也是联合国专门负责信息和通信技术领域的机构。FIDO 联盟的规范已得到 ITU 成员的批准，包括国家政府部门和全球领先的信息和通信技术公司。这些新的 ITU-T 建议由 ITU 的安全标准化专家组 ITU-T 第 17 组负责。&lt;/p&gt;

&lt;p&gt;FIDO 联盟标准开发高级主任 David Turner 表示：“FIDO 联盟通过基于公钥密码学的开放标准改进在线认证，使认证比密码或一次性验证码更强大且更易于使用。我们履行这一使命的一种方式是将成熟的技术规范提交给 ITU-T 等国际公认的标准组织进行正式标准化。ITU-T 的认可显示了 FIDO 认证技术的成熟度，并且与我们与万维网联盟（W3C）的网络标准化工作相辅相成。”&lt;/p&gt;

&lt;p&gt;ITU-T 第 17 组组长 Heung Youl Youm 表示：“FIDO UAF 和 CTAP 规范的前身于 2018 年首次被 ITU 标准采纳。ITU-T 第 17 组将继续加强与 FIDO 联盟的合作。这两个最近被采纳为 ITU 标准的 FIDO 联盟规范在金融等各个行业广泛应用，以基于公钥密码学和各种用户验证方法提供强大的在线认证。这些新的 ITU 标准将为这两个 FIDO 规范在 193 个 ITU 成员国家范围内的采纳提供具体的基础。”&lt;/p&gt;

&lt;p&gt;ITU-T 工作组“身份管理和远程生物特征体系结构与机制”（Q10/17）的报告人 Abbie Barbir 表示：“我们在 ITU-T 第 17 组的工作组很高兴与 FIDO 联盟合作推动最先进安全技术的标准化。这项工作将有助于解决密码的安全限制，并将世界更接近无密码解决方案。”&lt;/p&gt;

&lt;p&gt;现在成为 ITU-T 建议的规范包括：&lt;/p&gt;

&lt;p&gt;FIDO UAF 1.2（ITU-T 建议 X.1277.2）。这一移动标准通过使用生物特征和其他方式对用户进行本地设备上的身份验证，实现了无需密码的身份验证。&lt;/p&gt;

&lt;p&gt;CTAP 2.1（ITU-T 建议 X.1278.2）。作为 FIDO2 规范的一部分，与 W3C 网络身份验证标准一起，允许在支持 FIDO2 的浏览器和操作系统上通过 USB、NFC 或 BLE 使用外部认证器（FIDO 安全密钥、移动设备）进行无密码、二次因素或多因素身份验证。&lt;/p&gt;

&lt;p&gt;有关 FIDO 联盟和 FIDO 身份验证的更多信息，请访问&lt;a href="http://www.fidoalliance.org" rel="nofollow" target="_blank"&gt;http://www.fidoalliance.org&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;有关 ITU-T SG 17 的更多信息，请访问&lt;a href="https://www.itu.int/en/ITU-T/studygroups/2022-2024/17/Pages/default.aspx" rel="nofollow" target="_blank"&gt;https://www.itu.int/en/ITU-T/studygroups/2022-2024/17/Pages/default.aspx&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;关于 FIDO 联盟：
FIDO（Fast IDentity Online）联盟成立于 2012 年 7 月，旨在解决强身份验证技术之间的互操作性问题，并解决用户在创建和记住多个用户名和密码时遇到的问题。FIDO 联盟通过更简单、更强大的身份验证标准来改变认证的性质，定义了一组开放、可扩展、可互操作的机制，减少对密码的依赖。FIDO 身份验证在认证在线服务时更强大、更私密、更易于使用。&lt;/p&gt;

&lt;p&gt;关于 ITU-T SG 17：
国际电信联盟 ITU 的三个部门之一是 ITU 电信标准化部门（ITU-T）。ITU-T 负责协调成员国、私营部门成员和学术界成员之间的电信和信息通信技术标准，如用于网络安全的 X.509，用于机器学习的 Y.3172 和 Y.3173，以及用于视频压缩的 H.264/MPEG-4 AVC。&lt;/p&gt;

&lt;p&gt;来自：&lt;a href="https://fidoalliance.org/fido-alliance-publishes-guidance-for-deploying-passkeys-in-the-enterprise/" rel="nofollow" target="_blank"&gt;https://fidoalliance.org/fido-alliance-publishes-guidance-for-deploying-passkeys-in-the-enterprise/&lt;/a&gt;&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Thu, 06 Jul 2023 17:28:26 +0800</pubDate>
      <link>https://yubikey.cn/topics/19</link>
      <guid>https://yubikey.cn/topics/19</guid>
    </item>
    <item>
      <title>Web Authentication API 分享</title>
      <description>&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;: 此项功能仅在安全上下文 (HTTPS) 及支持的浏览器中可用。&lt;/p&gt;

&lt;p&gt;Web Authentication API 继承自 Credential Management API，使用公钥密码学赋能强身份验证，不需要手机短信就能实现无密码验证和安全的双因素验证。&lt;/p&gt;
&lt;h2 id="Web Authentication相关概念及用例"&gt;Web Authentication 相关概念及用例&lt;/h2&gt;
&lt;p&gt;Web Authentication API（也称作 WebAuthn）使用非对称（公钥）加密替代密码或短信进行网站注册、身份验证以及双因素验证，其优点包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;防网络钓鱼&lt;/strong&gt;：通过创建虚假登录网站的攻击形式将不再可行，因为签名会随着网站 origin（域、协议和端口）的变化而改变。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;减少数据泄露的危害&lt;/strong&gt;：开发人员不需要对公钥进行哈希处理，即使攻击者获取了用于身份验证的公钥，也无法完成验证，因为缺少了私钥。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;免受密码攻击&lt;/strong&gt;：部分用户可能会重复使用密码，攻击者可借此获取其他网站的密码（如通过数据泄漏）。此外，密码相较于数字签名也更容易被暴力破解。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;许多网站已经包含了用户注册、登录的页面，而 Web Authentication API 则能够取代这些已有网页，或作为补充。与其他的 Credential Management API 形式相类似，Web Authentication API 的两个基本方法分别对应于注册和登录：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;navigator.credentials.create() – 当使用 publicKey 选项时，该方法会创建新的凭证，可用于注册新账号，或将新的非对称密钥对凭证与已有账户关联。&lt;/li&gt;
&lt;li&gt;navigator.credentials.get() – 当使用 publicKey 选项时，该方法会使用现有的凭证集对服务进行身份验证，可以是用户登录或作为双因素身份验证的一种形式。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：create() 和 get() 都要求安全上下文（即服务器通过 HTTPS 连接或为本地服务器），而且如果浏览器没有在安全上下文中运作，也无法使用。&lt;/p&gt;

&lt;p&gt;在基础实现中，create() 和 get() 会从服务器接收一个大随机数，称为挑战，用私钥签名后将挑战返回服务器。这个过程向服务器证明了用户拥有身份验证所需要的私钥，且没有任何密码在网络上传输。&lt;/p&gt;

&lt;p&gt;为了了解 create() 和 get() 方法的实际应用，我们首先需要理解这两个方法处在浏览器之外的以下两个部分之间：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1.服务器 – Web Authentication API 是用来在服务器（也称为服务或依赖方）上注册新的凭证，而后在同一服务器上使用相同凭证来验证用户身份。&lt;/li&gt;
&lt;li&gt;2.认证器 – 凭证的创建与存储都在被称为认证器的设备上完成。对于身份验证来说，这是一个新的概念。使用密码进行身份验证时，密码由用户记忆，无需其他设备；而使用 Web Authentication 时，密码被密钥对取代，且存储于认证器中。认证器可内嵌于用户代理商、操作系统（如 Windows Hello），也可以是物理令牌（如 USB 或蓝牙安全密钥）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="注册"&gt;注册&lt;/h3&gt;
&lt;p&gt;注册过程通常包含六个步骤，图 1 所示是对注册过程所需数据的简要概述。创建注册请求所需的字段、可选字段及其意义的完整信息可查看 PublicKeyCredentialCreationOptions 字典。同样，所有的响应字段可以在 PublicKeyCredential 接口中查看（其中 PublicKeyCredential.response 是 AuthenticatorAttestationResponse 接口）。注意，大部分 JavaScript 程序员在创建应用程序时只会关注步骤 1 和 5，也就是调用 create() 函数与返回。但是，步骤 2、3、4 对于了解浏览器和认证器中的处理过程以及返回数据的意义都至关重要。
&lt;img src="https://cdn.yubikey.cn/photo/goll/89dd1ac3-c1c2-4c55-977b-71065558852a.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;

图 1 – WebAuthn 注册流程及各步骤中的相关重要数据

&lt;p&gt;首先，即图中步骤 0 所示，应用程序发起注册请求，该请求的协议与格式均不在 Web Authentication API 的范围内。&lt;/p&gt;

&lt;p&gt;后面的注册步骤如下：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;服务器发送挑战、用户信息及依赖方信息&lt;/strong&gt; – 服务器将挑战、用户信息和依赖方信息发送到 JavaScript 程序。这里并没有指定与服务器通信的协议，且不在 Web Authentication API 的范围内。通常情况下，服务器通信协议是 REST over HTTPS（可能会使用 XMLHttpRequest 或 Fetch），但也可以用 SOAP、RFC 2549 或几乎任何安全的协议。从服务器接收到的参数将传递给 create()，基本上不会作任何变动，即使有变动也很少。create() 会返回一个 Promise，最终解析为包含 AuthenticatorAttestationResponse 的 PublicKeyCredential。需要注意的是，挑战须为随机数缓存（至少 16 字节），且必须在服务器上生成以确保注册过程的安全性。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;浏览器向认证器调用 authenticatorMakeCredential()&lt;/strong&gt; – 在浏览器内部，浏览器会验证参数并用默认值补全缺少的参数，然后这些参数会变为 AuthenticatorResponse.clientDataJSON。其中最重要的参数之一是 origin，该参数是 clientData 的一部分，随后服务器会加以验证。调用 create() 的参数与 clientDataJSON 的 SHA-256 哈希一起传递到认证器（这个过程只有哈希被传输，因为认证器可能是通过低带宽的 NFC 或蓝牙连接，认证器只需要对哈希进行签名以确保未被篡改）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;认证器创建新密钥对与证明&lt;/strong&gt; – 在进行下一步之前，认证器通常会以某种形式要求用户确认，如输入 PIN、指纹、虹膜扫描等等，以证明用户在场且允许注册。完成用户确认之后，认证器会生成新的非对称密钥对，并将私钥安全存储以供将来验证使用。公钥则将作为证明的一部分，然后使用创建过程中就已刻录于认证器内的私钥进行签名。私钥具有一个证书链，可验证信任根。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;认证器将数据返回浏览器&lt;/strong&gt; – 新的公钥、全局唯一的凭证 id 及其他证明数据返回到浏览器，成为 attestationObject。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;浏览器生成最终数据，应用程序将响应发送到服务器&lt;/strong&gt; – create() Promise 解析为 PublicKeyCredential，其中包含全局唯一的凭证 id PublicKeyCredential.rawId 以及包含 AuthenticatorResponse.clientDataJSON 和 AuthenticatorAttestationResponse.attestationObject 的响应 AuthenticatorAttestationResponse。PublicKeyCredential 发送回服务器，可使用任何想要的格式和协议（注意，ArrayBuffer 类型的属性需要使用 base64 或类似编码方式进行编码）。&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;服务器验证并完成注册&lt;/strong&gt; – 最后，服务器需要执行一系列检查以确保注册完成且未被篡改。其中包括：
&lt;br&gt;i. 验证接收到的挑战与发送的挑战相同&lt;br&gt;
ii.确保 origin 与预期一致&lt;br&gt;
iii.使用对应认证器型号的证书链验证 clientDataHash 的签名和证明&lt;br&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;完整的验证步骤可查看 Web Authentication API 规范。检查完成后，服务器会将与用户账户相关联的新公钥进行存储，以供将来用户希望使用公钥进行身份验证时使用。&lt;/p&gt;
&lt;h3 id="验证"&gt;验证&lt;/h3&gt;
&lt;p&gt;用户在 WebAuthn 注册完成之后就可以进行身份验证（或者说登录）。验证的流程与注册流程相似，图 2 所示也与图 1 相类似。但是，二者的主要区别在于：1）验证不需要用户或依赖方信息；2）验证使用之前生成的密钥对来创建断言，而不是用认证器生产时就刻录进去的密钥对来创建证明。和上文一样，以下是对验证流程的简要概述，并不会详细描述 WebAuthn API 的每个环节或细节。验证所需的数据可查看 PublicKeyCredentialRequestOptions 字典，返回的数据可查看 PublicKeyCredential 接口（其中 PublicKeyCredential.response 是 AuthenticatorAssertionResponse 接口）。
&lt;img src="https://cdn.yubikey.cn/photo/goll/19a5dd08-bb2e-438e-80ff-9d0fc948ac47.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;

图 2 –WebAuthn 验证流程及各步骤中的相关重要数据

&lt;p&gt;后面的验证步骤如下：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;服务器发送挑战 – 服务器发送挑战到 JavaScript 程序。这里并没有指定与服务器通信的协议，且不在 Web Authentication API 的范围内。通常情况下，服务器通信协议是 REST over HTTPS（可能会使用 XMLHttpReques 或 Fetch），但也可以用 SOAP、RFC 2549 或几乎任何安全的协议。从服务器接收到的参数将传递给 get()，基本上不会作任何变动，即使有变动也很少。需要注意的是，挑战须为随机数缓存（至少 16 字节），且必须在服务器上生成以确保验证过程的安全性。&lt;/li&gt;
&lt;li&gt;浏览器向认证器调用 authenticatorGetCredential() - 在浏览器内部，浏览器会验证参数并用默认值补全缺少的参数，然后这些参数会变为 AuthenticatorResponse.clientDataJSON。其中最重要的参数之一是 origin，该参数是 clientData 的一部分，随后服务器会加以验证。调用 get() 的参数与 clientDataJSON 的 SHA-256 哈希一起传递到认证器（这个过程只有哈希被传输，因为认证器可能是通过低带宽的 NFC 或蓝牙连接，认证器只需要对哈希进行签名以确保未被篡改）。&lt;/li&gt;
&lt;li&gt;认证器创建断言 – 认证器找到匹配依赖方 ID 的凭证，并提示用户同意验证。上述两步成功后，认证器会使用注册调用时为该账户生成的私钥给 clientDataHash 和 authenticatorData 进行签名，从而创建新的断言。&lt;/li&gt;
&lt;li&gt;认证器将数据返回浏览器 – 认证器将 authenticatorData 和断言签名返回到浏览器。&lt;/li&gt;
&lt;li&gt;浏览器生成最终数据，应用程序将响应发送到服务器 – 浏览器将 Promise 解析为 PublicKeyCredential，其中含有包含 AuthenticatorAssertionResponse 的 PublicKeyCredential.response。这些数据具体使用何种协议和格式来传输返回至服务器，主要取决于 JavaScript 应用程序。&lt;/li&gt;
&lt;li&gt;服务器验证并完成身份验证 – 服务器接收到验证请求的结果后，会对响应进行验证，包括：
&lt;br&gt;i.使用注册请求中存储的公钥验证认证器的签名。&lt;br&gt;
ii.确保认证器签名的挑战与服务器生成的挑战一致。&lt;br&gt;
iii.检查依赖方 ID 与预期一致&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;验证断言的完整步骤可查看 Web Authentication API 规范。验证完成后，服务器会注意到用户身份已被验证。此步骤超出了 Web Authentication API 规范的范围，但有一个选项是给用户会话删除新 cookie。&lt;/p&gt;
&lt;h3 id="接口"&gt;接口&lt;/h3&gt;
&lt;p&gt;Credential
提供关于实体的信息，作为信任决策的先决条件。&lt;/p&gt;

&lt;p&gt;CredentialsContainer
公开请求凭证的方法，并在成功登录或退出时通知用户代理。该接口可通过 Navigator.credentials 访问。Web Authentication 规范在 create() 和 get() 方法中添加了 publicKey 成员，分别是为了创建新的公钥对与获取密钥对验证。&lt;/p&gt;

&lt;p&gt;PublicKeyCredential
该接口提供关于公钥/私钥对的信息，是一个使用防网络钓鱼与数据泄露的非对称密钥对进行服务登录的凭证，能够有效取代密码。&lt;/p&gt;

&lt;p&gt;AuthenticatorResponse
AuthenticatorAttestationResponse 和 AuthenticatorAssertionResponse 的基接口，二者为密钥对提供加密信任根。二者分别由 CredentialsContainer.create() 和 CredentialsContainer.get() 返回，这些子接口包含来自浏览器的信息，如挑战、origin。二者都可以通过 PublicKeyCredential.response 返回。&lt;/p&gt;

&lt;p&gt;AuthenticatorAttestationResponse
PublicKeyCredential 传递时，由 CredentialsContainer.create() 返回，该接口还能为已生成的新密钥对提供加密信任根。&lt;/p&gt;

&lt;p&gt;AuthenticatorAssertionResponse
PublicKeyCredential 传递时，由 CredentialsContainer.get() 返回，该接口还能向服务证明拥有密钥对且验证请求有效、已批准同意。&lt;/p&gt;
&lt;h3 id="选项"&gt;选项&lt;/h3&gt;
&lt;p&gt;PublicKeyCredentialCreationOptions
传递到 CredentialsContainer.create() 的选项。&lt;/p&gt;

&lt;p&gt;PublicKeyCredentialRequestOptions
传递到 CredentialsContainer.get() 的选项。&lt;/p&gt;
&lt;h3 id="举例"&gt;举例&lt;/h3&gt;
&lt;p&gt;Demo 网站&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;•&lt;a href="https://www.mozilla.org/en-US/" rel="nofollow" target="_blank" title=""&gt;Mozilla Demo 及源代码&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;•&lt;a href="https://webauthn.cn" rel="nofollow" target="_blank" title=""&gt;Webauhtn cn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;•&lt;a href="https://webauthn.io" rel="nofollow" target="_blank" title=""&gt;https://webauthn.io/ Demo 及源代码&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;•&lt;a href="https://github.com/webauthn-open-source" rel="nofollow" target="_blank" title=""&gt;github.com/webauthn-open-source及客户端源代码和服务器源代码&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="用例"&gt;用例&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;，出于安全考虑，如果浏览器窗口在调用过程中失去焦点，web authentication 调用 create() 和 get() 会被取消。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sample arguments for registration&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;createCredentialDefaultArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// Relying Party (a.k.a. - Service):&lt;/span&gt;
&lt;span class="na"&gt;rp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Acme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;

        &lt;span class="c1"&gt;// User:&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;john.p.smith@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John P. Smith&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;

        &lt;span class="na"&gt;pubKeyCredParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;alg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
        &lt;span class="p"&gt;}],&lt;/span&gt;

        &lt;span class="na"&gt;attestation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;direct&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="c1"&gt;// must be a cryptographically random number sent from a server&lt;/span&gt;
            &lt;span class="mh"&gt;0x8C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xFF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x91&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xC1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xE9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xB9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x4E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x2E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="mh"&gt;0x71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x9D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xD5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xA7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x7E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xEF&lt;/span&gt;
        &lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// sample arguments for login&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;getCredentialDefaultArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="c1"&gt;// allowCredentials: [newCredential] // see below&lt;/span&gt;
&lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="c1"&gt;// must be a cryptographically random number sent from a server&lt;/span&gt;
&lt;span class="mh"&gt;0x79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xEE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xEE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xB9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xC3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xC2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mh"&gt;0xE3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xF3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xAB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x3B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x2E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xD5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x81&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xE2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xA6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x7D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x74&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x50&lt;/span&gt;
&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// register / create a new credential&lt;/span&gt;
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createCredentialDefaultArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NEW CREDENTIAL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// normally the credential IDs available for an account would come from a server&lt;/span&gt;
        &lt;span class="c1"&gt;// but we can just copy them from above...&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;idList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nfc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ble&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}];&lt;/span&gt;
        &lt;span class="nx"&gt;getCredentialDefaultArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowCredentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;idList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getCredentialDefaultArgs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;assertion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ASSERTION&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assertion&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ERROR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="规范"&gt;规范&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://w3c.github.io/webauthn/" rel="nofollow" target="_blank" title=""&gt;Web Authentication: An API for accessing Public Key Credentials&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="浏览器兼容性"&gt;浏览器兼容性&lt;/h2&gt;&lt;h2 id="Credential"&gt;Credential&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/mdn/browser-compat-data/issues/new?mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWeb_Authentication_API&amp;amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EMDN+page+report+details%3C%2Fsummary%3E%0A%0A*+Query%3A+%60api.Credential%60%0A*+Report+started%3A+2022-05-30T09%3A47%3A25.177Z%0A%0A%3C%2Fdetails%3E&amp;amp;title=api.Credential+-+%3CSUMMARIZE+THE+PROBLEM%3E&amp;amp;template=data-problem.yml" rel="nofollow" target="_blank" title=""&gt;在 Github 上报告该兼容性数据的相关问题&lt;/a&gt;
&lt;img src="https://cdn.yubikey.cn/photo/goll/d7c76832-5005-46fc-b1e9-30529fc9f6ce.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="CredentialsContainer"&gt;CredentialsContainer&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/mdn/browser-compat-data/issues/new?mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWeb_Authentication_API&amp;amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EMDN+page+report+details%3C%2Fsummary%3E%0A%0A*+Query%3A+%60api.CredentialsContainer%60%0A*+Report+started%3A+2022-05-30T09%3A47%3A25.186Z%0A%0A%3C%2Fdetails%3E&amp;amp;title=api.CredentialsContainer+-+%3CSUMMARIZE+THE+PROBLEM%3E&amp;amp;template=data-problem.yml" rel="nofollow" target="_blank" title=""&gt;在 Github 上报告该兼容性数据的相关问题&lt;/a&gt;
&lt;img src="https://cdn.yubikey.cn/photo/goll/8b3afb60-e221-4712-9fd0-5b55fc81d424.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="PublicKeyCredential"&gt;PublicKeyCredential&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/mdn/browser-compat-data/issues/new?mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWeb_Authentication_API&amp;amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EMDN+page+report+details%3C%2Fsummary%3E%0A%0A*+Query%3A+%60api.PublicKeyCredential%60%0A*+Report+started%3A+2022-05-30T09%3A47%3A25.197Z%0A%0A%3C%2Fdetails%3E&amp;amp;title=api.PublicKeyCredential+-+%3CSUMMARIZE+THE+PROBLEM%3E&amp;amp;template=data-problem.yml" rel="nofollow" target="_blank" title=""&gt;在 Github 上报告该兼容性数据的相关问题&lt;/a&gt;
&lt;img src="https://cdn.yubikey.cn/photo/goll/0302342d-fe3b-40db-b92d-527a20978afd.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="AuthenticatorResponse"&gt;AuthenticatorResponse&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/mdn/browser-compat-data/issues/new?mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWeb_Authentication_API&amp;amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EMDN+page+report+details%3C%2Fsummary%3E%0A%0A*+Query%3A+%60api.AuthenticatorResponse%60%0A*+Report+started%3A+2022-05-30T09%3A47%3A25.206Z%0A%0A%3C%2Fdetails%3E&amp;amp;title=api.AuthenticatorResponse+-+%3CSUMMARIZE+THE+PROBLEM%3E&amp;amp;template=data-problem.yml" rel="nofollow" target="_blank" title=""&gt;在 Github 上报告该兼容性数据的相关问题&lt;/a&gt;
&lt;img src="https://cdn.yubikey.cn/photo/goll/4beda2ca-2a53-45ab-8a5e-8eab94404dce.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="AuthenticatorAttestationResponse"&gt;AuthenticatorAttestationResponse&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/mdn/browser-compat-data/issues/new?mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWeb_Authentication_API&amp;amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EMDN+page+report+details%3C%2Fsummary%3E%0A%0A*+Query%3A+%60api.AuthenticatorAttestationResponse%60%0A*+Report+started%3A+2022-05-30T09%3A47%3A25.211Z%0A%0A%3C%2Fdetails%3E&amp;amp;title=api.AuthenticatorAttestationResponse+-+%3CSUMMARIZE+THE+PROBLEM%3E&amp;amp;template=data-problem.yml" rel="nofollow" target="_blank" title=""&gt;在 Github 上报告该兼容性数据的相关问题&lt;/a&gt;
&lt;img src="https://cdn.yubikey.cn/photo/goll/f258cf76-57bb-4736-80dd-b20641013527.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="AuthenticatorAssertionResponse"&gt;AuthenticatorAssertionResponse&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/mdn/browser-compat-data/issues/new?mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWeb_Authentication_API&amp;amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EMDN+page+report+details%3C%2Fsummary%3E%0A%0A*+Query%3A+%60api.AuthenticatorAssertionResponse%60%0A*+Report+started%3A+2022-05-30T09%3A47%3A25.219Z%0A%0A%3C%2Fdetails%3E&amp;amp;title=api.AuthenticatorAssertionResponse+-+%3CSUMMARIZE+THE+PROBLEM%3E&amp;amp;template=data-problem.yml" rel="nofollow" target="_blank" title=""&gt;在 Github 上报告该兼容性数据的相关问题&lt;/a&gt;
&lt;img src="https://cdn.yubikey.cn/photo/goll/c3d0da2a-dc85-480f-ad7b-ec4f023930ed.png?imageView2/2/w/1920/q/100" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;文章来源：&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API" rel="nofollow" target="_blank"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API&lt;/a&gt;&lt;/p&gt;</description>
      <author>goll</author>
      <pubDate>Mon, 30 May 2022 18:04:05 +0800</pubDate>
      <link>https://yubikey.cn/topics/14</link>
      <guid>https://yubikey.cn/topics/14</guid>
    </item>
  </channel>
</rss>
