透過表單自動填入功能使用密碼金鑰登入

建立可利用密碼金鑰的登入體驗,同時也能支援現有的密碼使用者。

密碼金鑰可取代密碼,讓使用者在網路上的帳戶更加安全、簡單又好用。不過,從密碼驗證轉換為密碼金鑰驗證,可能會使使用者體驗變得複雜。使用表單自動填入功能建議密碼金鑰,有助於打造一致的使用體驗。

使用密碼金鑰後,使用者只要透過指紋、臉部或裝置 PIN 碼,就能登入網站。

理想情況下,使用者不需要輸入密碼,驗證流程也只需要一個單一登入按鈕即可。使用者輕觸按鈕後,系統會彈出帳戶選取器對話方塊,使用者可以選擇帳戶,解鎖畫面進行驗證及登入。

不過,從密碼轉換為密碼金鑰驗證可能會比較困難。雖然使用者會改用密碼金鑰,但仍會有人使用密碼,因此網站必須同時支援這兩種使用者。使用者不必記得自己已在哪些網站上改用密碼金鑰,因此要求使用者事先選取要使用的驗證方法,會導致使用者體驗不佳。

密碼金鑰也是一項新技術,解釋這些功能並確保使用者能輕鬆使用,對網站來說可能相當困難。我們可以利用熟悉的使用者體驗,為密碼自動填入功能解決這兩個問題。

條件式 UI

如要為密碼金鑰和密碼使用者提供高效的使用者體驗,您可以在自動填入建議中加入密碼金鑰。這稱為「條件式 UI」,屬於 WebAuthn 標準的一部分。

使用者輕觸使用者名稱輸入欄位後,系統就會彈出自動填入建議對話方塊,並醒目顯示已儲存的密碼金鑰,以及密碼自動填入建議。使用者可以選擇帳戶,並使用裝置螢幕鎖定功能登入。

這樣一來,使用者可以透過現有表單登入網站,就像沒有任何變化一樣,但如果使用者有安全金鑰,則可享有額外的安全性優勢

運作方式

如要使用密碼金鑰進行驗證,請使用 WebAuthn API

密碼金鑰驗證流程中的四個元件如下: 使用者:

  • 後端:您的後端伺服器,用於儲存帳戶資料庫,其中儲存了公開金鑰和密碼金鑰的其他中繼資料。
  • 前端:與瀏覽器通訊並將擷取要求傳送至後端的前端。
  • 瀏覽器:執行 JavaScript 的使用者瀏覽器。
  • 驗證器:使用者的驗證器,用於建立及儲存密碼金鑰。這可能與瀏覽器位於同一裝置上 (例如使用 Windows Hello 時),也可能位於手機等其他裝置上。
密碼金鑰驗證圖表
  1. 使用者一進入前端,就會向後端要求挑戰,以便透過密碼金鑰進行驗證,並呼叫 navigator.credentials.get() 以啟動密碼金鑰驗證程序。這會傳回 Promise
  2. 當使用者將游標移至登入欄位時,瀏覽器會顯示密碼自動填入對話方塊,其中包含密碼金鑰。如果使用者選取密碼金鑰,系統會顯示驗證對話方塊。
  3. 使用者使用裝置的螢幕鎖定功能驗證身分後,承諾會解析,並將公開金鑰憑證傳回至前端。
  4. 前端會將公開金鑰憑證傳送至後端。後端會根據資料庫中相符的帳戶公開金鑰,驗證簽名。如果成功,使用者就會登入。

透過表單自動填入功能使用密碼金鑰驗證

當使用者想要登入時,您可以發出條件式 WebAuthn get 呼叫,指出自動填入建議中可能包含密碼金鑰。對 WebAuthnnavigator.credentials.get() API 發出條件式呼叫不會顯示 UI,並會一直處於待處理狀態,直到使用者從自動填入建議中選取要登入的帳戶為止。如果使用者選擇密碼金鑰,瀏覽器會使用憑證解析承諾,而不是填寫登入表單。接著,網頁就負責讓使用者登入。

為表單輸入欄位加上註解

視需要在使用者名稱 input 欄位中加入 autocomplete 屬性。接著附加 usernamewebauthn 做為符記,讓系統建議密碼金鑰。

<input type="text" name="username" autocomplete="username webauthn" ...>

特徵偵測

在叫用條件式 WebAuthn API 呼叫之前,請檢查以下事項:

  • 瀏覽器支援使用 PublicKeyCredential 的 WebAuthn。

瀏覽器支援

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

資料來源

  • 瀏覽器支援使用 PublicKeyCredential.isConditionalMediationAvailable()WebAuthn 條件式 UI

瀏覽器支援

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

資料來源

// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.​​isConditionalMediationAvailable) {  
  // Check if conditional mediation is available.  
  const isCMA = await PublicKeyCredential.​​isConditionalMediationAvailable();  
  if (isCMA) {  
    // Call WebAuthn authentication  
  }  
}  

從 RP 伺服器擷取挑戰

從 RP 伺服器擷取呼叫 navigator.credentials.get() 所需的驗證問題:

  • challenge:ArrayBuffer 中伺服器產生的驗證問題。你必須提供這個值來防範重送攻擊。請務必在每次登入嘗試時產生新的驗證,並在一段時間後或在登入嘗試未通過驗證後忽略該驗證。可視為 CSRF 權杖。
  • allowCredentials:此驗證可接受的憑證陣列。傳遞空陣列,讓使用者從瀏覽器顯示的清單中選取可用的密碼金鑰。
  • userVerification:指出使用裝置螢幕鎖定功能進行使用者驗證的結果為 "required""preferred""discouraged"。預設值為 "preferred",表示驗證工具可能略過使用者驗證程序。將其設為 "preferred" 或省略屬性。

使用 conditional 標記呼叫 WebAuthn API,以便驗證使用者

呼叫 navigator.credentials.get() 開始等待使用者驗證。

// To abort a WebAuthn call, instantiate an `AbortController`.
const abortController = new AbortController();

const publicKeyCredentialRequestOptions = {
  // Server generated challenge
  challenge: ****,
  // The same RP ID as used during registration
  rpId: 'example.com',
};

const credential = await navigator.credentials.get({
  publicKey: publicKeyCredentialRequestOptions,
  signal: abortController.signal,
  // Specify 'conditional' to activate conditional UI
  mediation: 'conditional'
});
  • rpId:RP ID 是網域,網站可以指定其網域或可註冊的後置字串。這個值必須與建立密碼金鑰時使用的 rp.id 相符。

請務必指定 mediation: 'conditional',以便將要求設為條件式。

將傳回的公開金鑰憑證傳送至 RP 伺服器

使用者選取帳戶並同意使用裝置的螢幕鎖定功能後,系統會解析承諾,並將 PublicKeyCredential 物件傳回 RP 前端。

承諾遭拒的原因有很多,您需要根據 Error 物件的 name 屬性,妥善處理錯誤:

  • NotAllowedError:使用者已取消作業。
  • 其他例外狀況:發生不明錯誤。瀏覽器會向使用者顯示錯誤對話方塊。

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

  • id:經驗證密碼金鑰憑證的 base64url 編碼 ID。
  • rawId:憑證 ID 的 ArrayBuffer 版本。
  • response.clientDataJSON:用於儲存用戶端資料的 ArrayBuffer。這個欄位包含多項資訊,例如驗證問題和 RP 伺服器必須驗證的來源。
  • response.authenticatorData:驗證器資料的 ArrayBuffer。這個欄位包含 RP ID 等資訊。
  • response.signature:簽名的 ArrayBuffer。這個值是憑證的核心元素,需要在伺服器上進行驗證。
  • response.userHandle:ArrayBuffer,當中包含建立時設定的使用者 ID。如果伺服器必須選擇要使用的 ID 值,或是後端想避免建立憑證 ID 索引,則可使用這個值而非憑證 ID。
  • authenticatorAttachment:如果憑證來自本機裝置,就會傳回 platform。否則,cross-platform 會在 使用者使用手機登入時特別有用。如果使用者需要使用手機登入,不妨提示他們在本機裝置上建立密碼金鑰
  • type:這個欄位一律設為 "public-key"

如果您使用程式庫在 RP 伺服器上處理公開金鑰憑證物件,建議您先使用 base64url 部分編碼,再將整個物件傳送至伺服器。

驗證簽名

在伺服器上收到公開金鑰憑證時,請將其傳遞至 FIDO 程式庫,以便處理物件。

使用 id 屬性查詢相符的憑證 ID (如果您需要判斷使用者帳戶,請使用 userHandle 屬性,也就是您在建立憑證時指定的 user.id)。查看憑證的 signature 是否可以使用儲存的公開金鑰驗證。為此,建議您使用伺服器端程式庫或解決方案,而非自行編寫程式碼。您可以在 awesome-webauth GitHub 存放區中找到開放原始碼程式庫

憑證經過比對公開金鑰驗證後,請讓使用者登入。

請按照「伺服器端密碼金鑰驗證」中的詳細操作說明進行

資源