建立可使用密碼金鑰的登入體驗,同時仍能操作現有密碼使用者。
密碼金鑰可取代密碼,讓網路上的使用者帳戶更安全、更簡單、更易於使用。但是,從密碼式驗證轉換為密碼金鑰式驗證可能會降低使用者體驗。使用表單自動填入建議密碼金鑰,有助於打造整合式體驗。
為什麼要使用表單自動填入功能,透過密碼金鑰登入?
有了密碼金鑰,使用者就能透過指紋、臉孔或裝置 PIN 碼登入網站。
在理想情況下,沒有密碼使用者,驗證流程可以像單一登入按鈕一樣簡單。使用者輕觸按鈕後,帳戶選取器對話方塊會彈出,使用者可以挑選帳戶、解鎖螢幕進行驗證並登入。
不過,從密碼轉換成密碼金鑰式驗證可能並不容易。當使用者改用密碼金鑰時,使用密碼和網站的使用者必須滿足這兩種使用者的需求。不應讓使用者記得自己已將哪些網站改用密碼金鑰,因此如果要求使用者選擇在前端使用哪種方法,使用者體驗就會不佳。
密碼金鑰也是一項新技術,說明這些行為並確保使用者安心使用,對網站而言是一大挑戰。我們可以利用熟悉的使用者體驗自動填入密碼功能來解決這兩項問題。
條件式 UI
如要為密碼金鑰和密碼使用者打造高效率的使用者體驗,您可以在自動填入建議中加入密碼金鑰。這稱為條件式 UI,是 WebAuthn 標準的一部分。
使用者輕觸使用者名稱輸入欄位時,自動填入建議對話方塊會彈出,說明已儲存的密碼金鑰,並提供密碼自動填入建議。使用者就能選擇所需帳戶,並使用裝置螢幕鎖定功能登入。
如此一來,使用者就能透過現有表單登入您的網站,即使沒有變更,也可以使用密碼金鑰多添一層安全保障 (如果有的話)。
運作方式
如要以密碼金鑰進行驗證,請使用 WebAuthn API。
密碼金鑰驗證流程的四個元件如下:使用者:
- 後端:您的後端伺服器,保留帳戶資料庫儲存公開金鑰和其他中繼資料。
- 前端:這個前端會與瀏覽器通訊,並將擷取要求傳送至後端。
- 瀏覽器:執行 JavaScript 的使用者瀏覽器。
- Authenticator:建立及儲存密碼金鑰的使用者驗證器。可能與瀏覽器位於相同裝置 (例如使用 Windows Hello 時) 或其他裝置 (例如手機)。
- 使用者進入前端時,後端就會要求後端以使用密碼金鑰進行驗證,並呼叫
navigator.credentials.get()
以使用密碼金鑰進行驗證。這會傳回Promise
。 - 當使用者將遊標置於登入欄位時,瀏覽器會顯示密碼自動填入對話方塊,包括密碼金鑰。如果使用者選取密碼金鑰,系統就會顯示驗證對話方塊。
- 使用者使用裝置的螢幕鎖定功能驗證身分後,系統就會解析入侵,並將公開金鑰憑證傳回前端。
- 前端將公開金鑰憑證傳送至後端。後端會根據資料庫中相符帳戶的公開金鑰驗證簽名。如果成功,使用者就會登入。
透過表單自動填入功能使用密碼金鑰驗證
當使用者要登入時,您可以發出條件式 WebAuthn get
呼叫,指出密碼金鑰可納入自動填入建議中。針對 WebAuthn 的 navigator.credentials.get()
API 發出的條件式呼叫不會顯示 UI,且會一直處於待處理狀態,直到使用者從自動填入建議中挑選要登入的帳戶為止。如果使用者選擇密碼金鑰,瀏覽器就會以憑證來解析承諾,而非填寫登入表單。接著網頁會負責讓使用者登入
為表單輸入欄位加上註解
視需要在使用者名稱 input
欄位中新增 autocomplete
屬性。將 username
和 webauthn
附加為權杖,讓系統提供密碼金鑰建議。
<input type="text" name="username" autocomplete="username webauthn" ...>
特徵偵測
叫用條件式 WebAuthn API 呼叫之前,請先確認下列事項:
- 瀏覽器透過
PublicKeyCredential
支援 WebAuthn。
- 瀏覽器透過
PublicKeyCredenital.isConditionalMediationAvailable()
支援 WebAuthn 條件式 UI。
// 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 伺服器擷取挑戰
從呼叫 navigator.credentials.get()
所需的 RP 伺服器擷取挑戰:
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 存放區中找到開放原始碼程式庫。
使用相符的公開金鑰驗證憑證後,請使用者登入。
如需詳細操作說明,請參閱「伺服器端密碼金鑰驗證」一文