在昨天的探索中,我們繪製了一張 FIDO2 的宏觀作戰地圖,理解了 WebAuthn (軟體 API) 與 CTAP (硬體通訊) 如何協同作戰。今天,我們將暫時收起地圖,拿出放大鏡,聚焦在地圖上離我們開發者最近的核心區域——WebAuthn API。
要讀懂任何一部經典,都必須先熟悉其中的角色與關鍵詞彙。WebAuthn 也不例外。在我們動手編寫任何程式碼之前,必須先建立一個清晰的「世界觀」,搞懂誰是誰、它們各自的職責,以及它們之間傳遞的「信物」代表什麼。
WebAuthn 的運作就像一齣舞台劇,由三個核心角色協力完成一場關於「信任」的演出。
信賴方 (Relying Party, RP)
驗證器 (Authenticator)
客戶端 (Client)
WebAuthn 的所有互動,都圍繞著這兩個核心的「儀式」展開。
1. 註冊儀式 (Attestation):信任的誕生
「Attestation」意為「證明」。這是在使用者帳戶與一個新的驗證器之間建立信任連結的過程。
API 呼叫流程:
options
物件。options
物件傳送給前端頁面。navigator.credentials.create({ publicKey: options })
,將後端傳來的選項交給瀏覽器。create()
函式成功後回傳一個 PublicKeyCredential
物件,前端將此物件送回後端進行驗證。
PublicKeyCredential
是在註冊與驗證過程中,WebAuthn API 回傳給我們網站的頂層資料容器。可以把它想像成一個公文袋,裡面裝著所有重要的文件。
// Day 03: 註冊儀式的前端呼叫範例
async function startRegistration() {
// 步驟 1 & 2: 從我們的後端伺服器獲取註冊選項
const response = await fetch('/generate-registration-options');
const options = await response.json();
// WebAuthn 要求 challenge 等二進位資料必須是 ArrayBuffer
// 需要一個輔助函數來轉換 Base64URL 編碼的 challenge
options.challenge = bufferDecode(options.challenge);
options.user.id = bufferDecode(options.user.id);
try {
// 步驟 3: 呼叫 WebAuthn API,觸發瀏覽器與驗證器的互動
const credential = await navigator.credentials.create({
publicKey: options
});
// 步驟 5: 將瀏覽器回傳的憑證送回後端進行驗證
await sendRegistrationToServer(credential);
alert('註冊成功!');
} catch (err) {
console.error('註冊失敗:', err);
}
}
2. 驗證儀式 (Assertion):信任的證明
「Assertion」意為「斷言」。這是使用者在登入時,向伺服器證明自己確實擁有私鑰的過程。
API 呼叫流程:
options
物件。options
物件傳送給前端頁面。navigator.credentials.get({ publicKey: options })
。get()
函式成功後回傳 PublicKeyCredential
物件,前端將其送回後端。// Day 03: 驗證儀式的前端呼叫範例
async function startLogin() {
// 步驟 1 & 2: 從後端獲取登入選項
const response = await fetch('/generate-authentication-options');
const options = await response.json();
options.challenge = bufferDecode(options.challenge);
try {
// 步驟 3: 呼叫 WebAuthn API,請求使用者進行簽章
const assertion = await navigator.credentials.get({
publicKey: options
});
// 步驟 5: 將簽章後的結果送回後端進行驗證
await sendLoginAssertionToServer(assertion);
alert('登入成功!');
window.location.href = "/dashboard"; // 導向登入後頁面
} catch (err) {
console.error('登入失敗:', err);
}
}
今天,我們沒有深入後端驗證的複雜細節,而是建立了一份至關重要的詞彙表與角色名單。我們認識了舞台上的三大主角:信賴方 (RP)、驗證器 (Authenticator) 與客戶端 (Client),也清楚看見了它們在註冊 (Attestation) 與登入 (Assertion) 這兩大儀式中的 API 互動流程。
有了這些共同語言與清晰的流程概念,我們就做好了萬全的準備。
明天,我們將正式開始解構 WebAuthn 的第一個核心儀式——註冊 (Registration)。我們將使用序列圖 (Sequence Diagram) 來描繪這些角色如何互動,並深入探討後端伺服器(RP)在發起註冊請求與驗證註冊結果時,必須完成的每一個關鍵步驟。
參考資料: