為無密碼登入建立密碼金鑰

密碼金鑰可讓使用者帳戶更安全、更簡單、更容易使用。

使用密碼金鑰取代密碼,是讓網站讓使用者帳戶更安全、更簡單、更容易使用,並且無需密碼的絕佳方式。使用者只要透過密碼金鑰,就能使用指紋、臉部或裝置 PIN 碼登入網站或應用程式。

您必須先建立密碼金鑰、與使用者帳戶建立關聯,並將密碼金鑰儲存在伺服器上,使用者才能使用密碼金鑰登入。

運作方式

在下列任一情況下,系統可能會要求使用者建立密碼金鑰:

  • 使用者使用密碼登入時。
  • 使用者使用其他裝置的密碼金鑰登入 (也就是 authenticatorAttachmentcross-platform)。
  • 使用者可以透過專屬頁面管理密碼金鑰。

如要建立密碼金鑰,請使用 WebAuthn API

密碼金鑰註冊流程的四個元件如下:

  • 後端:您的後端伺服器,保留帳戶資料庫儲存公開金鑰和其他中繼資料。
  • 前端:與瀏覽器通訊並將擷取要求傳送至後端。
  • 瀏覽器:執行 JavaScript 的使用者瀏覽器。
  • 驗證器:使用者的驗證器,用於建立及儲存密碼金鑰。這可能包括瀏覽器所在裝置 (例如使用 Windows Hello 時) 或其他裝置 (例如手機) 上的密碼管理工具。
密碼金鑰註冊流程圖

將新密碼金鑰新增至現有使用者帳戶的流程如下:

  1. 使用者登入網站。
  2. 使用者登入後,會要求在前端建立密碼金鑰,例如按下「建立密碼金鑰」按鈕。
  3. 前端會從後端要求資訊以建立密碼金鑰,例如使用者資訊、驗證問題和要排除的憑證 ID。
  4. 前端會呼叫 navigator.credentials.create() 來建立密碼金鑰。這個呼叫會傳回承諾。
  5. 使用者同意使用裝置的螢幕鎖定功能後,系統就會建立密碼金鑰。承諾已解析,並將公開金鑰憑證傳回至前端。
  6. 前端將公開金鑰憑證傳送至後端,並儲存憑證 ID 和與使用者帳戶相關聯的公開金鑰,供日後的驗證使用。

相容性

大多數瀏覽器都支援 WebAuthn,但仍有小幅差異。請參閱裝置支援 - passkeys.dev,瞭解哪些瀏覽器和作業系統組合支援建立密碼金鑰。

建立新的密碼金鑰

以下說明前端在收到建立新密碼金鑰的要求時,應如何運作。

特徵偵測

在顯示「建立新密碼金鑰」按鈕之前,請確認以下事項:

  • 瀏覽器透過 PublicKeyCredential 支援 WebAuthn。

瀏覽器支援

  • Chrome:67.
  • Edge:18。
  • Firefox:60。
  • Safari:13。

資料來源

  • 裝置支援使用 PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() 的平台驗證器 (可建立密碼金鑰,並使用密碼金鑰進行驗證)。

瀏覽器支援

  • Chrome:67.
  • Edge:18。
  • Firefox:60。
  • Safari:13。

資料來源

瀏覽器支援

  • Chrome:108。
  • Edge:108。
  • Firefox:119。
  • Safari:16 歲。

資料來源

// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.  
// `​​isConditionalMediationAvailable` means the feature detection is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&  
    PublicKeyCredential.​​isConditionalMediationAvailable) {  
  // Check if user verifying platform authenticator is available.  
  Promise.all([  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),  
    PublicKeyCredential.​​isConditionalMediationAvailable(),  
  ]).then(results => {  
    if (results.every(r => r === true)) {  
      // Display "Create a new passkey" button  
    }  
  });  
}  

在符合所有條件之前,這個瀏覽器不會支援密碼金鑰。直到那時,系統才會顯示「Create a new passkey」(建立新密碼金鑰) 按鈕。

從後端擷取重要資訊

當使用者按下按鈕時,請擷取重要資訊,以便從後端呼叫 navigator.credentials.create()

  • challenge:伺服器為此註冊產生的 ArrayBuffer 挑戰。這是必填欄位,但在註冊期間不會用到 (除非要進行認證),這是本文未涵蓋的進階主題。
  • user.id:使用者的專屬 ID。這個值必須是 ArrayBuffer,且不得包含個人識別資訊,例如電子郵件地址或使用者名稱。每個帳戶產生的 16 位元組隨機值通常很好。
  • user.name:這個欄位應包含使用者能夠認得的帳戶專屬 ID,例如電子郵件地址或使用者名稱。這會顯示在帳戶選取器中。(如果使用使用者名稱,請使用密碼驗證中的相同值)。
  • user.displayName:這個欄位為必填欄位,可讓使用者更容易理解。這個名稱不必是唯一值,也可以是使用者選擇的名稱。如果您的網站沒有適合用於此處的值,請傳遞空字串。視瀏覽器而定,這可能會顯示在帳戶選取器中。
  • excludeCredentials:提供已註冊的憑證 ID 清單,防止註冊同一部裝置。transports 成員 (如有提供) 應包含註冊各個憑證時呼叫 getTransports() 的結果。

呼叫 WebAuthn API 以建立密碼金鑰

呼叫 navigator.credentials.create() 即可建立新密碼金鑰。API 會傳回承諾,等待使用者與顯示強制回應對話方塊的互動。

瀏覽器支援

  • Chrome:60 分鐘。
  • Edge:18。
  • Firefox:60。
  • Safari:13.

資料來源

const publicKeyCredentialCreationOptions = {
  challenge: *****,
  rp: {
    name: "Example",
    id: "example.com",
  },
  user: {
    id: *****,
    name: "john78",
    displayName: "John",
  },
  pubKeyCredParams: [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}],
  excludeCredentials: [{
    id: *****,
    type: 'public-key',
    transports: ['internal'],
  }],
  authenticatorSelection: {
    authenticatorAttachment: "platform",
    requireResidentKey: true,
  }
};

const credential = await navigator.credentials.create({
  publicKey: publicKeyCredentialCreationOptions
});

// Encode and send the credential to the server for verification.  

上述未說明的參數如下:

  • rp.id:RP ID 是網域,網站可以指定其網域或可註冊的後置字串。舉例來說,如果 RP 的來源是 https://login.example.com:1337,RP ID 可以是 login.example.comexample.com。如果 RP ID 指定為 example.com,使用者可以在 login.example.comexample.com 的任何子網域上進行驗證。

  • rp.name:RP 的名稱。

  • pubKeyCredParams:這個欄位會指定 RP 支援的公開金鑰演算法。建議您將其設為 [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]。這會指定對使用 P-256 和 RSA PKCS#1 的 ECDSA 提供支援,並支援這些 ECDSA。

  • authenticatorSelection.authenticatorAttachment:如果這個密碼金鑰的建立程序是密碼升級 (例如登入後的促銷活動),請將其設為 "platform""platform" 表示 RP 需要平台驗證器 (嵌入平台裝置的驗證器),系統不會提示使用者插入 USB 安全金鑰等。使用者可以選擇較簡單的建立密碼金鑰。

  • authenticatorSelection.requireResidentKey:將其設為布林值「true」。可偵測的憑證 (本機金鑰) 會將使用者資訊儲存至密碼金鑰,並讓使用者在驗證時選取帳戶。如要進一步瞭解可供搜尋的憑證,請參閱可供搜尋的憑證深入解析

  • authenticatorSelection.userVerification:指出使用裝置螢幕鎖定功能進行使用者驗證的結果是 "required""preferred" 還是 "discouraged"。預設值為 "preferred",表示驗證工具可能略過使用者驗證程序。請將這個屬性設為 "preferred" 或省略屬性。

將傳回的公開金鑰憑證傳送至後端

使用者同意使用裝置的螢幕鎖定功能後,系統會建立密碼金鑰,並解析承諾,將 PublicKeyCredential 物件傳回至前端。

Promise 可能會因為各種原因遭到拒絕。您可以檢查 Error 物件的 name 屬性,藉此處理這些錯誤:

  • InvalidStateError:裝置上已存在密碼金鑰。系統不會向使用者顯示錯誤對話方塊,而且網站也不應將這項行為視為錯誤,因為使用者希望註冊本機裝置,而系統也已完成註冊。
  • NotAllowedError:使用者已取消作業。
  • 其他例外狀況:發生不明錯誤。瀏覽器會向使用者顯示錯誤對話方塊。

公開金鑰憑證物件包含下列屬性:

  • id:所建立密碼金鑰的 Base64URL 編碼 ID。這個 ID 可在驗證時,協助瀏覽器判斷裝置上是否有相符的密碼金鑰。這個值必須儲存在後端資料庫中。
  • rawId:憑證 ID 的 ArrayBuffer 版本。
  • response.clientDataJSON:ArrayBuffer 編碼的用戶端資料。
  • response.attestationObject:ArrayBuffer 編碼的認證物件。當中包含重要資訊,例如 RP ID、旗標和公開金鑰。
  • authenticatorAttachment:如果這組憑證是在支援密碼金鑰的裝置上建立,就會傳回 "platform"
  • type:這個欄位一律設為 "public-key"

若您使用程式庫在後端處理公開金鑰憑證物件,建議您先使用 base64url 進行部分編碼,再將整個物件傳送至後端。

儲存憑證

在後端收到公開金鑰憑證後,將憑證傳送給 FIDO 程式庫來處理物件。

接著,您可以將從憑證擷取的資訊儲存到資料庫,以供日後使用。下列清單包含一些可以儲存的一般屬性:

  • 憑證 ID (主索引鍵)
  • 使用者 ID
  • 公開金鑰

公開金鑰憑證也包含下列資訊,您可能會想將這些資訊儲存在資料庫中:

請按照「伺服器端密碼金鑰註冊」中的詳細操作說明

如要驗證使用者,請參閱「透過表單自動填入功能使用密碼金鑰登入」。

資源