使用 Signal API 讓密碼金鑰與伺服器上的憑證保持一致

發布日期:2024 年 11 月 12 日

WebAuthn Signal API 可讓信賴方向已連結的密碼金鑰供應器傳送現有憑證。如此一來,支援密碼金鑰的供應商就能更新或從儲存空間中移除錯誤或已撤銷的密碼金鑰,以免繼續提供給使用者。

相容性

電腦版 Chrome 132 以上版本支援 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 就能將儲存空間中的剩餘憑證通知密碼金鑰供應器。密碼金鑰提供者可以自行決定如何處理這項信號,但系統會移除不符合這份清單的密碼金鑰,以免使用者在登入時看到沒有相關憑證的密碼金鑰。

這個 API 應在使用者在 RP 上刪除密碼金鑰每次登入時叫用,以便密碼金鑰提供者保留同步化的密碼金鑰清單。

信號更新使用者名稱和顯示名稱

// 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 密碼管理工具中更新密碼金鑰的相關 metadata 時,系統會顯示這個對話方塊。

摘要

Signal API 可消除意外登入失敗的可能性,協助您打造更優質的密碼金鑰體驗。透過 Signal API,依賴方可以傳送現有憑證清單及其中繼資料,讓密碼金鑰供應器保持同步。

如要進一步瞭解密碼金鑰,請參閱「使用密碼金鑰登入帳戶,不必輸入密碼」。