Web 身份驗證擴充套件
Web 身份驗證 API 擁有一套擴充套件系統——在憑據建立 (navigator.credentials.create()) 或身份驗證 (navigator.credentials.get()) 操作期間可請求的額外功能。本文介紹瞭如何請求 WebAuthn 擴充套件、檢索有關這些請求響應的資訊以及可用的擴充套件——包括瀏覽器支援和預期的輸入和輸出。
如何使用 WebAuthn 擴充套件
當呼叫 navigator.credentials.create() 或 navigator.credentials.get() 時,啟動 WebAuthn 流程所需的 publicKey 物件引數可以包含一個 extensions 屬性。extensions 的值本身是一個物件,其屬性是依賴方希望在其呼叫的方法中使用任何擴充套件的輸入值。
在後臺,輸入由使用者代理和/或身份驗證器處理。
例如,在 create() 呼叫的 publicKey 物件中,我們可能希望請求使用兩個擴充套件
credProps擴充套件。依賴方設定credProps以請求瀏覽器告知依賴方憑據在註冊後是否是常駐/可發現的。當使用publicKey.authenticatorSelection.residentKey = "preferred"呼叫create()時,這很有用。要請求它,您還需要在瀏覽器建立憑據時設定publicKey.extensions.credProps = true,並且根據使用的身份驗證器型別,它將是可發現的(例如,FIDO2 身份驗證器通常會使其可發現;FIDO1/U2F 安全金鑰將是不可發現的)。credProps僅由使用者代理處理。minPinLength擴充套件允許依賴方請求身份驗證器的最小 PIN 長度。這要求extensions.minPinLength設定為true。minPinLength由身份驗證器處理,使用者代理僅用於將輸入資料傳遞給它。
const publicKey = {
challenge: new Uint8Array([117, 61, 252, 231, 191, 241 /* … */]),
rp: { id: "acme.com", name: "ACME Corporation" },
user: {
id: new Uint8Array([79, 252, 83, 72, 214, 7, 89, 26]),
name: "jamiedoe",
displayName: "Jamie Doe",
},
pubKeyCredParams: [{ type: "public-key", alg: -7 }],
authenticatorSelection: {
residentKey: "preferred",
},
extensions: {
credProps: true,
minPinLength: true,
},
};
然後我們可以將 publicKey 物件傳遞給 create() 呼叫以啟動憑據建立流程
navigator.credentials.create({ publicKey });
檢索擴充套件請求結果
如果成功,create() 呼叫將返回一個 Promise,它將解析為一個 PublicKeyCredential 物件。一旦擴充套件處理完成,處理結果將在響應中傳達(儘管並非在所有情況下都如此——擴充套件可能沒有輸出)。
navigator.credentials
.create({ publicKey })
.then((publicKeyCred) => {
const myClientExtResults = publicKeyCred.getClientExtensionResults();
// myClientExtResults will contain the output of processing
// the "credProps" extension
const authData = publicKeyCred.response.getAuthenticatorData();
// authData will contain authenticator data, which will include
// authenticator extension processing results, i.e., minPinLength
})
.catch((err) => {
console.error(err);
});
如上述程式碼片段所示,有兩種不同的地方可以找到輸出擴充套件結果
-
您可以透過呼叫
PublicKeyCredential.getClientExtensionResults()方法找到客戶端(使用者代理)擴充套件處理的結果。這會返回一個map,其中每個條目都以擴充套件識別符號字串作為鍵,以客戶端處理擴充套件的輸出作為值。在上面的示例中,如果瀏覽器支援credProps擴充套件並且它被正確處理,myClientExtResultsmap 物件將包含一個條目"credProps",其值為{ rk: true }。這將驗證所建立的憑據確實是可發現的。 -
您可以在操作的身份驗證器資料中找到身份驗證器擴充套件處理的結果
- 對於成功
create()呼叫返回的PublicKeyCredential,這可以透過呼叫publicKeyCredential.response.getAuthenticatorData()來返回。 - 對於成功
get()呼叫返回的PublicKeyCredential,這可以在publicKeyCredential.response.authenticatorData屬性中找到。
身份驗證器資料採用具有一致結構的
ArrayBuffer形式——參見身份驗證器資料。身份驗證器擴充套件結果資料始終位於末尾的一個部分,作為一個表示結果的 CBOR 對映。有關完整身份驗證器資料結構的詳細描述,請參閱AuthenticatorAssertionResponse.authenticatorData。回到我們的示例,如果依賴方被授權接收
minPinLength值,身份驗證器資料將包含以下形式的表示:"minPinLength": uint。 - 對於成功
可用擴充套件
下面的擴充套件並不代表所有可用擴充套件的詳盡列表。我們選擇記錄我們已知已標準化並至少被一個渲染引擎支援的擴充套件。
appid
- 可用範圍:身份驗證 (
get()) - 由:使用者代理 處理
- 規範:FIDO AppID 擴充套件 (appid)
允許依賴方請求先前使用舊版 FIDO U2F JavaScript API 註冊的憑據的斷言,避免重新註冊憑據的麻煩。appid 是該 API 中與 WebAuthn 中的 rpId 等效的(儘管請記住,appid 採用 URL 形式,而 rpId 採用域名形式)。
輸入
publicKey 的 extensions 屬性必須包含一個 appid 屬性,其值是舊版 API 中使用的應用程式識別符號。例如
({
extensions: {
appid: "https://accounts.example.com",
},
});
您還必須在 publicKey 的 allowCredentials 屬性中列出 FIDO U2F 憑據 ID,例如
({
allowCredentials: [
{
id: arrayBuffer, // needs to contain decoded binary form of id
transports: ["nfc", "usb"],
type: "public-key",
},
],
});
輸出
如果 appid 成功用於斷言,則輸出 appid: true,否則輸出 appid: false。
appidExclude
- 可用範圍:註冊 (
create()) - 由:使用者代理 處理
- 規範:FIDO AppID 排除擴充套件 (appidExclude)
允許依賴方在註冊期間排除包含先前使用舊版 FIDO U2F JavaScript API 註冊的指定憑據的身份驗證器。這是必需的,因為預設情況下 excludeCredentials 欄位的內容被假定為 WebAuthn 憑據。使用此擴充套件時,您可以在 excludeCredentials 中包含舊版 FIDO U2F 憑據,它們將被識別為舊版憑據。
輸入
publicKey 的 extensions 屬性必須包含一個 appidExclude 屬性,其值是請求按舊版 FIDO U2F 憑據排除身份驗證器的依賴方識別符號。例如
({
extensions: {
appidExclude: "https://accounts.example.com",
},
});
然後您可以在 publicKey 的 excludeCredentials 屬性中列出 FIDO U2F 憑據,例如
({
excludeCredentials: [
{
id: arrayBuffer, // needs to contain decoded binary form of id
transports: ["nfc", "usb"],
type: "public-key",
},
],
});
輸出
如果擴充套件已執行,則輸出 appidExclude: true,否則輸出 appidExclude: false。
credProps
- 可用範圍:註冊 (
create()) - 由:使用者代理 處理
- 規範:憑據屬性擴充套件 (credProps)
允許依賴方請求有關已建立憑據的附加資訊/屬性。目前,這僅在呼叫 create() 並將 publicKey.authenticatorSelection.residentKey 設定為 "preferred" 時有用;它請求有關已建立憑據是否可發現的資訊。
輸入
publicKey 的 extensions 屬性必須包含一個值為 true 的 credProps 屬性
({
extensions: {
credProps: true,
},
});
您還必須將 authenticatorSelection.requireResidentKey 設定為 true,這表示需要常駐金鑰。
({
authenticatorSelection: {
requireResidentKey: true,
},
});
輸出
如果註冊的 PublicKeyCredential 是客戶端可發現憑據,則輸出以下內容
({
credProps: {
rk: true,
},
});
如果輸出中 rk 設定為 false,則憑據是伺服器端憑據。如果輸出中沒有 rk,則不知道憑據是客戶端可發現的還是伺服器端的。
credProtect
- 可用範圍:註冊 (
create()) - 由:身份驗證器 處理
- 規範:憑據保護 (credProtect)
允許依賴方在建立憑據時指定最低憑據保護策略。
輸入
publicKey 的 extensions 屬性必須包含一個 credentialProtectionPolicy 屬性,用於指定要建立憑據的保護級別,以及一個布林 enforceCredentialProtectionPolicy 屬性,用於指定 create() 呼叫是否應失敗而不是建立不符合指定策略的憑據
({
extensions: {
credentialProtectionPolicy: "userVerificationOptional",
enforceCredentialProtectionPolicy: true,
},
});
可用的 credentialProtectionPolicy 值如下
"userVerificationOptional"實驗性功能-
使用者驗證是可選的。傳送給身份驗證器進行處理的等效
credProtect值為0x01。 "userVerificationOptionalWithCredentialIDList"-
僅當憑據可發現(即,它是客戶端可發現的)時,使用者驗證才是可選的。傳送給身份驗證器進行處理的等效
credProtect值為0x02。 "userVerificationRequired"-
始終需要使用者驗證。傳送給身份驗證器進行處理的等效
credProtect值為0x03。
注意:Chromium 將預設使用 userVerificationOptionalWithCredentialIDList 或 userVerificationRequired,具體取決於請求型別
- 當建立憑據時,如果
residentKey設定為preferred或required,Chromium 將請求保護級別為userVerificationOptionalWithCredentialIDList。(設定requireResidentKey與required視為相同。)這確保了簡單地擁有安全金鑰不會允許查詢給定rpId的可發現憑據的存在。 - 此外,如果
residentKey為required且userVerification為preferred,則保護級別將提高到userVerificationRequired。這確保了擁有安全金鑰不會允許登入到不需要使用者驗證的網站。(這不是完全保護;網站仍應仔細考慮其使用者的安全性。) - 如果網站請求明確的
credProtect級別,則該級別將覆蓋這些預設值。如果安全金鑰的預設級別更高,這些預設值絕不會導致保護級別低於安全金鑰的預設級別。
假設 enforceCredentialProtectionPolicy 值為 true。在這種情況下,如果無法遵守策略(例如,它需要使用者驗證,但身份驗證器不支援使用者驗證),則 create() 呼叫將失敗。如果為 false,系統將盡最大努力建立符合策略的憑據,但如果不可能,它仍將盡可能建立符合策略的憑據。
輸出
如果 create() 呼叫成功,身份驗證器資料將包含以下形式的 credProtect 值表示,該值表示所設定的策略
({ credProtect: 0x01 });
largeBlob
- 可用範圍:註冊 (
create()) 和身份驗證 (get()) - 由:使用者代理 處理
- 規範:大 blob 儲存擴充套件 (largeBlob)
允許依賴方在身份驗證器上儲存與憑據關聯的 blob——例如,它可能希望直接儲存證書而不是執行集中式身份驗證服務。
輸入
在 create() 呼叫期間,publicKey 的 extensions 屬性必須包含一個具有以下物件結構的 largeBlob 屬性
({
extensions: {
largeBlob: {
support: "required",
},
},
});
support 屬性的值是一個字串,可以是以下之一
"preferred":如果可能,憑據將使用可以儲存 blob 的身份驗證器建立,但如果不能,它仍將建立一個。輸出的“supported”屬性報告身份驗證器儲存 blob 的能力。"required":憑據將使用身份驗證器建立以儲存 blob。如果無法做到這一點,create()呼叫將失敗。
在 get() 呼叫期間,publicKey 的 extensions 屬性必須包含一個具有兩個子屬性之一——read 或 write 的 largeBlob 屬性(如果兩者都存在,get() 將失敗)
read 屬性是一個布林值。值為 true 表示依賴方希望獲取與斷言憑據關聯的先前寫入的 blob
({
extensions: {
largeBlob: {
read: true,
},
},
});
write 屬性的值是 ArrayBuffer、TypedArray 或 DataView,表示依賴方希望與現有憑據一起儲存的 blob
({
extensions: {
largeBlob: {
write: arrayBuffer,
},
},
});
注意:要使寫入身份驗證操作成功,publicKey.allowCredentials 必須只包含一個表示您希望 blob 儲存在其旁邊的憑據的元素。
輸出
如果註冊的憑據能夠儲存 blob,則成功的 create() 呼叫會提供以下擴充套件輸出
({
largeBlob: {
supported: true, // false if it cannot store blobs
},
});
如果成功,get() 讀取呼叫會將 blob 作為 ArrayBuffer 在擴充套件輸出中提供
({
largeBlob: {
blob: arrayBuffer,
},
});
注意:如果失敗,將返回 largeBlob 物件,但不會出現 blob。
get() 寫入呼叫透過擴充套件輸出中的 written 布林值指示寫入操作是否成功。true 值表示已成功寫入關聯的身份驗證器,false 表示不成功。
({
largeBlob: {
written: true,
},
});
minPinLength
- 可用範圍:註冊 (
create()) - 由:身份驗證器 處理
- 規範:最小 PIN 長度擴充套件 (minPinLength)
允許依賴方請求身份驗證器的最小 PIN 長度。
輸入
publicKey 的 extensions 屬性必須包含一個值為 true 的 minPinLength 屬性
({
extensions: {
minPinLength: true,
},
});
輸出
如果依賴方被授權接收 minPinLength 值(如果其 rpId 存在於身份驗證器的授權依賴方列表中),則身份驗證器資料將包含以下形式的表示
({ minPinLength: uint });
如果依賴方未授權,則擴充套件將被忽略,並且不提供 "minPinLength" 輸出值。
payment
允許依賴方請求建立可用於安全支付確認的 WebAuthn 憑據——由依賴方和其他方共同使用;請參閱使用安全支付確認。
輸入
payment 擴充套件的輸入在 AuthenticationExtensionsPaymentInputs 字典中定義
isPayment-
一個布林值,指示擴充套件是否處於活動狀態。
rpID-
正在使用的憑據的依賴方 ID。僅在身份驗證時使用;不用於註冊。
topOrigin-
頂級框架的來源。僅在身份驗證時使用;不用於註冊。
payeeName-
如果存在,顯示給使用者的收款人名稱。僅在身份驗證時使用;不用於註冊。
payeeOrigin-
如果存在,顯示給使用者的收款人來源。僅在身份驗證時使用;不用於註冊。
total-
顯示給使用者的交易金額。僅在身份驗證時使用;不用於註冊。總額型別為 PaymentCurrencyAmount。
instrument-
顯示給使用者的工具詳細資訊。僅在身份驗證時使用;不用於註冊。工具型別為 PaymentCredentialInstrument。
輸出
None
prf
- 可用範圍:註冊 (
create()) 和身份驗證 (get()) - 由:使用者代理 處理
- 規範:偽隨機函式擴充套件 (prf)
允許依賴方從與憑據關聯的偽隨機函式 (PRF) 獲取一個或兩個輸入的輸出。PRF 實際上是一個隨機預言機——一個對任何給定輸入返回隨機值,但對相同輸入總是返回相同值的函式。
生成與使用者憑據關聯的隨機數的能力在許多加密應用程式中都很有用。例如,它可以用於生成對稱金鑰以加密敏感資料,並且只能由擁有種子和關聯身份驗證器的使用者解密。它也可以類似地用於建立端到端加密的對稱金鑰,該金鑰使用來自伺服器的值作為種子,並且對於該憑據和會話是唯一的。
該擴充套件允許您將 ArrayBuffer 或 TypedArray 型別的緩衝區值傳遞給身份驗證器,身份驗證器將返回使用關聯憑據的 PRF 評估該值的結果。這可以在斷言中完成,作為身份驗證工作流的一部分——指定要評估結果的一個或多個憑據。它也可以在建立憑據時完成;但支援在建立憑據時生成輸出的身份驗證器較少。
輸入
在 create() 呼叫期間,publicKey 的 extensions 屬性可能包含一個 prf 屬性,該屬性具有 eval 物件,其中包含 first 屬性和可選的 second 屬性。這些屬性是 ArrayBuffer 或 TypedArray 例項,其中包含要傳遞給憑據的 PRF 的值。
例如,以下定義可以在建立新憑據時使用,以便從伺服器提供的秘密建立新的對稱金鑰。
({
extensions: {
prf: {
eval: { first: new TextEncoder().encode("Salt for new symmetric key") },
},
},
});
如果需要為憑據建立兩個隨機值,例如在每次會話旋轉加密金鑰的工作流中,可以使用可選的 second 屬性。例如,在此類工作流中,在每個會話中您傳遞兩個鹽值:first 鹽值返回一個可用於解密上一會話資料的值,而 second 鹽值返回一個可用於加密此會話資料的值。在後續會話中,second 鹽值移動到 first 鹽值的位置,從而限制了特定鹽值可能被有效洩露的生命週期。
({
extensions: {
prf: {
eval: {
first: currentSessionKey, // salt for current session
second: nextSessionKey, // salt for next session
},
},
},
});
create() 呼叫可能因以下異常而拒絕
NotSupportedErrorDomExceptioneval物件中存在evalByCredential鍵。
請注意,在建立憑據時評估 PRF 可能不受支援,並且這將在輸出中報告。您仍然可以嘗試在斷言中評估 PRF,如下所示。
在 get() 呼叫期間,publicKey 的 extensions 屬性可能包含一個帶有 evalByCredential 子屬性的 prf 屬性。這是一個物件,將 Base64 URL 編碼的憑據 ID 對映到與上述相同形式的評估物件。換句話說,這允許您指定要評估不同憑據的值。
({
extensions: {
prf: {
evalByCredential: {
"<credentialId>": { first: bufferOne, second: bufferTwo },
// …
"<credentialId2>": {
first: anotherBufferOne,
second: anotherBufferTwo,
},
},
},
},
});
get() 呼叫可能因以下異常而拒絕
NotSupportedErrorDomException-
如果
eval是prf物件,或者當evalByCredential不為空時allowCredentials為空。 SyntaxErrorDomException-
evalByCredential中的任何鍵都是空字串或不是有效的 Base64 URL 編碼,或者與publicKey.allowCredentials中的某個元素的 ID 不匹配。
輸出
如果註冊的憑據支援在建立憑據時使用 PRF,則成功的 create() 呼叫會提供以下擴充套件輸出。
({
prf: {
enabled: true, // PRF can be used when creating credentials.
results: { first: outputBuffer1, second: outputBuffer2 },
},
});
enabled 屬性指示在建立憑據時是否可以使用 PRF。first 和 second 屬性包含評估輸入上的 first 和 second 的結果,如果未指定相應的輸入,則將省略 second。
如果身份驗證器不支援在建立時使用 PRF,則 create() 的輸出將如下所示
({
prf: {
enabled: false, // PRF cannot be used when creating credentials.
},
});
get() 返回與 create() 相同的 prf 物件,具有相同的結構,只是它省略了 enabled 鍵。該物件包含與使用者選擇的憑據的輸入相對應的 PRF 值。
({
prf: {
results: { first: outputBuffer1, second: outputBuffer2 },
},
});
請注意,enabled 僅作為 create() 的輸出存在,並指示在建立憑據時身份驗證器是否支援 PRF。如果身份驗證器根本不支援 PRF,則 get() 呼叫的結果將是
({
prf: {},
});
規範
| 規範 |
|---|
| Web Authentication:訪問公鑰憑證的 API - 第 3 級 # sctn-defined-extensions |
| 未知規範 # sctn-defined-extensions |
WebAuthn 擴充套件在許多地方都有指定。IANA 的 WebAuthn 擴充套件識別符號提供了所有擴充套件的登錄檔,但請記住,有些可能已被棄用。
瀏覽器相容性
WebAuthn 擴充套件的相容性資料已分解為兩個表——可在憑據註冊 (create()) 期間使用的擴充套件,以及可在身份驗證 (get()) 期間使用的擴充套件。某些擴充套件在這兩種操作期間均可使用。
api.CredentialsContainer.create.publicKey_option.extensions
載入中…
api.CredentialsContainer.get.publicKey_option.extensions
載入中…