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

发布时间:2024 年 11 月 12 日

借助 WebAuthn Signal API,信赖方可以向关联的通行密钥提供程序发送现有凭据信号。这样一来,支持通行密钥的提供程序就可以更新或从其存储空间中移除不正确或已撤消的通行密钥,以免再向用户提供这些通行密钥。

兼容性

从 Chrome 132 开始,桌面版 Chrome 支持 Signal API。Google 密码管理工具可以更新反映该信号的通行密钥。对于基于 Chrome 扩展程序的通行密钥提供程序,是否反映该信号取决于它们自己。

可在 Android 设备上运行的 Chrome 将稍后支持此功能。

Safari 支持,但尚未实现。Firefox 尚未分享其意见

背景

创建通行密钥(可检测到的凭据)时,用户名和显示名称等元数据会随私钥一起保存到通行密钥提供程序(例如密码管理工具)中,而公钥凭据会保存到依赖方 (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 可以告知通行密钥提供程序指定的凭据已被移除或不存在。如何处理此信号取决于通行密钥提供程序,但预计系统会移除关联的通行密钥,以便用户无法使用通行密钥登录,因为关联的凭据不存在。

当基于通行密钥的登录因缺少凭据而失败时,可以调用此 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",
      ...
    ]
  });
}

通过使用 RP ID、用户 ID 和存储的凭据的凭据 ID 列表调用 PublicKeyCredential.signalAllAcceptedCredentials(),RP 可以告知通行密钥提供程序其存储空间中剩余的凭据。通行密钥提供程序可以自行决定如何处理此信号,但系统应移除与此列表不匹配的通行密钥,以便用户在登录时不会看到没有关联凭据的通行密钥。

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

指明更新后的用户名和显示名称

// 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 可以告知通行密钥提供程序更新后的用户信息。通行密钥提供程序可以自行决定如何处理此信号,但用户拥有的通行密钥应该会更新为包含新用户信息。

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

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

摘要

Signal API 可帮助您消除意外登录失败的可能性,从而打造更好的通行密钥体验。借助 Signal API,依赖方可以发送现有凭据及其元数据的列表,以便保持通行密钥提供程序中的通行密钥保持同步。

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