通行金鑰的使用案例
本主題描述傳遞金鑰的一些使用案例。
使用案例 1:啟動載入
在網路上啟動帳戶。
1.1:驗證使用者
本節適用於信賴憑證者 (RP) 尚不知道誰正在控制用戶端裝置時。 RP 沒有瀏覽器成品可供 RP 使用(例如 Cookie 或本機記憶體中的認證識別碼),不過目前我們假設使用者具有具有 RP 的現有帳戶。
若要啟動帳戶,請為使用者提供登入頁面。
首先,要求使用者輸入其帳戶標識碼;通常是使用者名稱或電子郵件位址。
若要支援傳遞金鑰的自動填滿 UI,請務必:
username
將和webauthn
值新增至使用者名稱輸入字段上任何現有的自動完成批注。
<div>
<label for="username">Username:</label>
<input name="username" id="loginform.username"
autocomplete="username webauthn">
</div>
- 在頁面載入時,使用
if
語句來檢查是否可使用自動填入 UI(條件式中繼),然後使用 和userVerification: "preferred"
呼叫navigator.credentials.get()
mediation: "conditional"
。
<script>
(async () => {
if (
typeof window.PublicKeyCredential !== 'undefined'
&& typeof window.PublicKeyCredential.isConditionalMediationAvailable === 'function'
) {
const available = await PublicKeyCredential.isConditionalMediationAvailable();
if (available) {
try {
// Retrieve authentication options for `navigator.credentials.get()`
// from your server.
const authOptions = await getAuthenticationOptions();
// This call to `navigator.credentials.get()` is "set and forget."
// The Promise will resolve only if the user successfully interacts
// with the browser's autofill UI to select a passkey.
const webAuthnResponse = await navigator.credentials.get({
mediation: "conditional",
publicKey: {
...authOptions,
// See note about userVerification below.
userVerification: "preferred",
}
});
// Send the response to your server for verification, and
// authenticate the user if the response is valid.
await verifyAutoFillResponse(webAuthnResponse);
} catch (err) {
console.error('Error with conditional UI:', err);
}
}
}
})();
</script>
上述會導致下列情況發生:
- 從您的伺服器擷取驗證選項。 至少傳回隨機
challenge
且rpId
與這個驗證要求相關聯。 - 當使用者與您的 使用者名稱 字段互動時,瀏覽器和平臺會檢查是否存在可搭配信賴憑證者使用的複雜密鑰(在平臺驗證器中)。
- 如果是這種情況,則傳遞金鑰會呈現給使用者作為選擇的選項(以及其他可自動填入的認證,例如儲存在瀏覽器密碼管理員中的用戶名稱)。 瀏覽器/平臺可能會轉譯類似如下所示的UI。 雖然確切的外觀和風格會因平臺或尺寸而異:
- 如果使用者選取通行金鑰,則平臺 UI 會引導用戶進行(通常是生物特徵辨識型)使用者驗證檢查。
- 如果使用者成功通過使用者驗證,則
navigator.credentials.get()
呼叫會成功並傳回 WebAuthn 回應。 - 如果使用者選取複雜金鑰以外的認證,則瀏覽器/平台會選擇不同的適當動作(例如自動填入用戶名稱),而且
navigator.credentials.get()
呼叫無法解析。 - 如果使用者選取 [從另一部裝置傳遞密鑰] 選項(確切的文字會因平臺而稍有不同),則瀏覽器/平臺會引導使用者使用 FIDO2 安全性金鑰或跨裝置驗證 (CDA) 流程,以使用智慧手機或平板電腦傳遞 WebAuthn 回應給
navigator.credentials.get()
通話。 - 將 WebAuthn 回應傳送至您的伺服器,以進行驗證和其他安全性檢查。 如果所有檢查都成功,請為此用戶開始驗證的會話。
這就是為什麼這稱為 WebAuthn 的條件式 UI(或更常見的是自動填入 UI)模式的原因—引導使用者通過驗證的平臺驗證器 UI,或使用其手機,只有在使用者在此裝置上具有通行密鑰時才會顯示(或選擇「另一個裝置」選項)。
如您所見,在此模式中, navigator.credentials.get()
呼叫會成功,或不是因為它永遠不會解析。 如果成功,則呼叫的結果會顯示使用者標識碼和已簽署的 WebAuthn 判斷提示,信賴憑證者 (RP) 將用來驗證使用者。
如果呼叫失敗,則您應該執行 舊版 用戶驗證。 您將從這個第一頁取得使用者名稱,然後在後續頁面中,為使用者提供適當的進一步登入挑戰(例如密碼、回應 SMS 挑戰等)。 這些可能包括 帳戶復原 步驟,以防使用者忘記其密碼,或無法通過一般登入挑戰。 用戶通過所有登入挑戰之後,就會被視為已驗證並登入。
當使用者還沒有具有信賴憑證者 (RP) 的帳戶時,您通常會在登入頁面上提供選項來建立帳戶。 如果使用者選擇該選項,您將會從他們收集必要的資訊,以開啟新的帳戶。 如果他們成功開啟新的帳戶,則也會被視為已驗證並登入。
使用者登入后,可能是時候為他們設定新的複雜密鑰了。 針對下列任何案例執行此動作:
- 使用者透過傳遞非複雜金鑰登入挑戰(例如使用密碼)在裝置上啟動其帳戶。
- 使用者剛在信賴憑證者 (RP) 建立新帳戶,因此會被視為登入。
- 使用者使用的是複雜密鑰,但他們使用與目前使用的不同裝置(選取上述範例所示的「另一個裝置」)。 在傳回的 PublicKeyCredential 物件中檢查 authenticatorAttachment 屬性,即可確認此情況。
1.2:跨裝置驗證
如果使用者使用來自另一個裝置的通行密鑰(例如手機、平板電腦或 FIDO2 安全性密鑰),驗證回應中的 authenticatorAttachment 屬性會具有 值cross-platform
。
在該案例中,讓用戶選擇在其本機裝置上建立傳遞密鑰。 這將在未來產生更順暢的用戶體驗,因為使用者不需要使用其他裝置。
1.3:關於使用者驗證的附註
本指南會將userVerification設定為 preferred
,這表示會盡可能嘗試用戶驗證。
某些裝置,例如桌面電腦和較舊的膝上型計算機,可能沒有生物特徵辨識感測器。 在這些裝置上,如果 userVerification 設定為 required
,則系統可能會要求使用者使用複雜金鑰輸入其系統登入密碼。 這對他們來說可能令人沮喪。
使用 時 preferred
,某些平台驗證器一律會在裝置具有生物特徵辨識感測器時要求使用者驗證檢查,但可能會在沒有生物識別感測器的裝置上略過用戶驗證。
使用者驗證結果(在驗證器數據旗標中傳達)會反映實際的使用者驗證結果,而且應一律根據您在伺服器上的需求進行驗證。
1.4:選擇使用者加入通行密鑰
首先,使用其他登入方法確認使用者已足夠強式地進行驗證,包括多重要素驗證。
其次,請呼叫下列命令,確保使用者的裝置和操作系統 (OS) 組合支援通行密鑰:
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
如果支援複雜金鑰,則會傳回 true
。 如果不支援,則會傳回 false
,而且您應該中止複雜密鑰註冊流程。
提供選擇加入或「向上銷售」強制回應/插入式或頁面給提供選項的使用者,以建立通行密鑰:
提示
為了確保用戶獲得完全知情的同意,請考慮顯示 (或連結至) 較長的描述,說明能夠解除鎖定目前裝置的所有使用者都能夠存取信賴憑證者 (RP) 的帳戶。
如果使用者同意,請使用下列範例所示的選項進行呼叫 navigator.credentials.create()
:
navigator.credentials.create({
publicKey: {
rp: {
// User-friendly name of your service.
name: "Passkeys Developer",
// Relying party (RP) identifier (hostname/FQDN).
id: passkeys.contoso"
},
user: {
// Persistent, unique identifier for the user account in your backend.
id: Uint8Array.from("0525bc79-5a63-4e47-b7d1-597e25f5caba", c => c.charCodeAt(0)),
// User-friendly identifier often displayed to the user (for example, email address).
name: "amanda@contoso.com",
// Human-readable display name, sometimes displayed by the client.
displayName: "Amanda Brady"
},
// The challenge is a buffer of cryptographically random bytes generated on your backend,
// and should be tightly bound to the current user session.
challenge: Uint8Array.from("XZJscsUqtBH7ZB90t2g0EbZTZYlbSRK6lq7zlN2lJKuoYMnp7Qo2OLzD7xawL3s", c => c.charCodeAt(0)),
pubKeyCredParams: [
// An array of objects describing what public key types are acceptable to a server.
{
"type": "public-key",
"alg": -7 // EC P256
},
{
"type": "public-key",
"alg": -257 // RSA
}
],
excludeCredentials: [
// Array of credential IDs for existing passkeys tied to the user account.
// This avoids creating a new passkey in an authenticator that already has
// a passkey tied to the user account.
{
// Example only.
type: "public-key",
id: new Uint8Array([21, 31, 56, ...]).buffer
},
{
// Example only.
type: "public-key",
id: new Uint8Array([21, 31, 56, ...]).buffer
}
],
authenticatorSelection: {
// Tells the authenticator to create a passkey.
residentKey: "required",
// Tells the client/authenticator to request user verification where possible;
// for example, a biometric or a device PIN.
userVerification: "preferred"
},
"extensions": {
// Returns details about the passkey.
"credProps": true
}
}
})
注意
建議大多數信賴憑證者 (RPs) 未指定證明傳遞參數 attestation
(因此預設為 none),或改為明確使用 值 indirect
。 這可保證最精簡的用戶體驗(平臺可能會從使用者取得其他使用者的證明傳遞同意,這可能會造成由於使用者取消建立而造成較大比例的認證建立失敗)。
當 WebAuthn 呼叫解析時,將回應傳送至您的伺服器,並將傳回的公鑰和認證識別碼與先前驗證的使用者帳戶產生關聯。
使用案例 2:重新驗證
基於下列任何原因,可能需要使用複雜金鑰進行重新驗證:
- 用戶已註銷,現在想要再次登入。
- 用戶會話因閑置而過期,且使用者想要再次登入。
- 用戶即將執行敏感性動作,且需要重新確認用戶會話的控制。
若要在這些情況下重新驗證使用者,您將使用您在先前使用案例中設定的通行密鑰。 在這三種情況下,WebAuthn API 呼叫都相同,但您提供的 UI 處理稍有不同。 由於特定帳戶是由您指定,因此平臺不會提示用戶選取您服務上的不同帳戶。
2.1:敏感性動作
讓我們先查看 UI 的第三個原因,也就是重新驗證敏感性動作時,請檢查您是否有至少一個複雜密鑰的認證標識碼。
如果沒有這類認證識別碼可用,則提供適合重新驗證的傳統登入挑戰,例如:
提示
建議您在此登入挑戰頁面上,用戶無法變更其帳戶標識碼。 此外,登入挑戰應該是裝置未經授權使用者無法通過的專案。
另一方面,如果您發現使用者至少有一個複雜密鑰認證標識碼,則可以使用複雜金鑰進行重新驗證:
當使用者準備就緒時(在上述範例中,按兩下 [Go] 按鈕時,呼叫 navigator.credentials.get()
,傳入所有使用者的通行密鑰認證識別碼:
navigator.credentials.get({
publicKey: {
challenge: ...,
rpId: ...,
allowCredentials: [{
type: "public-key",
id: new UInt8Array([21, 31, 56, ...]).buffer,
}, {
type: "public-key",
id: new UInt8Array([21, 31, 56, ...]).buffer,
}, {
...
}],
// see note below
userVerification: "preferred",
}
});
注意
請務必閱讀先前使用案例中userVerification的指引。
如果使用者改為按兩下 [嘗試另一種方式],則您應該提供他們其他登入方法(密碼等)來重新驗證他們(假設使用者有這類其他登入方法可供他們使用)。
2.2:已過期的會話和註銷
現在,我們將檢查觸發重新驗證的情況,因為使用者已註銷,或信賴憑證者 (RP) 已過期使用者的會話。 為了方便這一點,RP 必須保留某種形式的使用者會話狀態,提醒他們先前登入的帳戶,即使他們認為使用者已註銷(這可以使用瀏覽器成品,例如 Cookie 或本機記憶體來達成)。
注意
信賴憑證者 (RP) 可能會選擇將註銷視為全面動作,因而刪除使用者身分識別的所有參考。 這類 RP 應該將後續登入視為帳戶啟動程式,並重複先前說明的步驟。
您身為 RP,接著可能會提供如下所示的登入頁面:
如果使用者按兩下 [使用不同的帳戶],則您應該輸入帳戶啟動程式流程,如先前使用案例所述,重複該處的步驟,平臺會讓用戶選取要使用的帳戶。
注意
在此情況下,您也應該讓用戶能夠完全移除建議的帳戶,而不要列在登入頁面上。
但是如果使用者按兩下 [登入身分] 按鈕,請檢查您是否至少有一個與使用者相關聯的複雜密鑰認證標識碼。 如果沒有可用的認證識別碼,則提供適合重新驗證的傳統登入挑戰,例如:
另一方面,如果您發現使用者至少有一個複雜密鑰認證標識碼,則可以使用複雜金鑰進行重新驗證:
當使用者準備就緒時(在上述範例中,當他們按兩下 [Go] 按鈕時,請 navigator.credentials.get()
呼叫 ,就像已經顯示一樣(也就是說,藉由傳入所有使用者的passkey認證標識符)。
如果使用者改為按兩下 [嘗試另一種方式],則您應該提供他們其他登入方法(密碼等)來重新驗證他們(假設使用者有這類其他登入方法可供他們使用)。
下一步
接下來,請參閱 傳遞金鑰的工具和連結庫。