使用 Signal API 确保通行密钥与服务器上的凭据保持一致

发布时间:2024 年 11 月 12 日;上次更新时间:2024 年 11 月 29 日

借助 WebAuthn Signal API,信赖方可以向已连接的通行密钥提供方传递与现有凭据相关的信号。借助此功能,提供支持的通行密钥提供方可以更新或移除其存储空间中错误或已撤消的通行密钥,从而不再向用户提供此类通行密钥。

兼容性

Chrome 在所有桌面平台和 Android 上均支持 Signal API。

Safari 支持,但尚未实现。Firefox 尚未分享其观点

Google 密码管理工具可以更新通行密钥,以反映该信号。桌面设备上基于 Chrome 扩展程序的通行密钥提供方会决定是否反映该信号。

背景

创建通行密钥(可发现的凭据)时,用户名和显示名称等元数据会与私钥一起保存到通行密钥提供方(例如密码管理工具),而公钥凭据会保存到信赖方 (RP) 的服务器。保存用户名和显示名称有助于用户在系统提示时确定要使用哪个提供的通行密钥进行登录。当用户拥有来自不同通行密钥提供商的两把以上的通行密钥时,此功能特别有用。

不过,在少数情况下,通行密钥提供方的通行密钥列表与服务器的凭据列表之间存在不一致,可能会导致混淆。

第一种情况是用户在服务器上删除凭据。这样一来,通行密钥提供程序中的通行密钥就不会受到影响。用户下次尝试使用通行密钥登录时,通行密钥提供方仍会向用户显示该通行密钥。不过,登录尝试会失败,因为服务器无法验证已删除的公钥。

第二种情况是用户在服务器上更新其用户名或显示名称。下次用户尝试登录时,即使密钥提供方中的密钥已在服务器上更新,但仍会显示旧的用户名和显示名。理想情况下,这些内容是同步的。

Signal API

Signal API 是一种 WebAuthn API,它允许 RP 向通行密钥提供方传递更改信号,从而解决这些不一致问题。有三种方法:

表明凭据不存在的信号

const credential = await navigator.credentials.get({ ... });
const payload = credential.toJSON();

const result = await fetch('/login', { ... });

// Detect authentication failure due to lack of the credential
if (result.status === 404) {
  // Feature detection
  if (PublicKeyCredential.signalUnknownCredential) {
    await PublicKeyCredential.signalUnknownCredential({
      rpId: "example.com",
      credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
    });
  } else {
    // Encourage the user to delete the passkey from the password manager nevertheless.
    ...
  }
}

通过使用 RP ID 和凭据 ID 调用 PublicKeyCredential.signalUnknownCredential(),RP 可以告知通行密钥提供方指定的凭据已被移除或不存在。通行密钥提供方会确定如何处理此信号,但应移除关联的通行密钥,以免用户尝试使用缺少关联凭据的通行密钥登录。

Browser Support

  • Chrome: 132.
  • Edge: 132.
  • Firefox: not supported.
  • Safari: 26.

Source

当基于通行密钥的登录因缺少凭据而失败时,可以调用此 API。这样,RP 就可以防止用户尝试使用没有关联凭据的通行密钥登录。与 signalAllAcceptedCredentials 不同,此方法不需要您传递整个凭据 ID 列表,因此只要用户未通过身份验证,就应使用此方法,以免泄露给定用户的通行密钥数量。

当通行密钥从 Chrome 的 Google 密码管理工具中删除时显示的对话框。
从 Chrome 中的 Google 密码管理工具中删除通行密钥时显示的对话框。

发出已保存凭据列表的信号

// After a user deletes a passkey or a user is signed in.

// Feature detection
if (PublicKeyCredential.signalAllAcceptedCredentials) {
  await PublicKeyCredential.signalAllAcceptedCredentials({
    rpId: "example.com",
    userId: "M2YPl-KGnA8", // base64url encoded user ID
    allAcceptedCredentialIds: [ // A list of base64url encoded credential IDs
      "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA",
      ...
    ]
  });
}

在用户登录或管理账号设置后使用 PublicKeyCredential.signalAllAcceptedCredentials()。您需要提供相应用户的所有有效凭据 ID 的列表。通行密钥提供方会将此列表与该信赖方的本地存储进行比较。如果密钥提供方在其存储空间中发现任何未包含在 allAcceptedCredentialIds 列表中的通行密钥,则会将其标记为“隐藏”。系统不再提供这些隐藏的通行密钥以用于登录或自动填充,但不会立即永久删除它们,以便在必要时进行恢复。 相反,通行密钥提供程序会恢复 allAcceptedCredentialIds 中标记为“隐藏”的通行密钥。这样,您的网站就可以恢复因错误而隐藏的通行密钥。

Browser Support

  • Chrome: 132.
  • Edge: 132.
  • Firefox: not supported.
  • Safari: 26.

Source

当用户在 RP 上删除通行密钥时以及每次登录时,都应调用此 API,以便通行密钥提供程序可以维护同步的通行密钥列表。

Signal 更新了用户名和显示名称

// After a user updated their username and/or display name
// or a user is signed in.

// Feature detection
if (PublicKeyCredential.signalCurrentUserDetails) {
  await PublicKeyCredential.signalCurrentUserDetails({
    rpId: "example.com",
    userId: "M2YPl-KGnA8", // base64url encoded user ID
    name: "a.new.email.address@example.com", // username
    displayName: "J. Doe"
  });
} else {
}

通过使用 RP ID、用户 ID、用户名和显示名称调用 PublicKeyCredential.signalCurrentUserDetails(),RP 可以将更新后的用户信息告知通行密钥提供方。通行密钥提供方会确定如何处理此信号,但应使用新的用户信息更新用户拥有的通行密钥。

Browser Support

  • Chrome: 132.
  • Edge: 132.
  • Firefox: not supported.
  • Safari: 26.

Source

此 API 可在用户更新用户名或显示名时以及每次登录时调用,以便通行密钥提供方能够使此信息与服务器保持同步。

当 Chrome 中的 Google 密码管理工具更新通行密钥元数据时显示的对话框。
在 Chrome 中,当 Google 密码管理工具中的通行密钥元数据更新时显示的对话框。

摘要

Signal API 可帮助您消除意外的登录失败情况,从而打造更好的通行密钥体验。借助 Signal API,信赖方可以传递现有凭据及其元数据的列表,以便让通行密钥提供方上的通行密钥保持同步。

如需详细了解通行密钥,请参阅使用通行密钥实现无密码登录