คู่มือนี้จะอธิบายวิธีลงทะเบียนพาสคีย์ให้ผู้ใช้โดยอัตโนมัติและไม่มีการแจ้งเตือนทันทีหลังจากลงชื่อเข้าใช้ด้วยรหัสผ่านสำเร็จ ซึ่งจะช่วยลดความยุ่งยากและเพิ่มการใช้พาสคีย์
ช่วงเวลาที่เหมาะสมในการกระตุ้น
การสร้างพาสคีย์อัตโนมัติ (หรือที่เรียกว่าการสร้างแบบมีเงื่อนไขหรือการโปรโมตหลังการเข้าสู่ระบบแบบไม่แสดงข้อความ) ต้องทริกเกอร์ทันทีหลังจากลงชื่อเข้าใช้แบบเต็มรูปแบบที่เกี่ยวข้องกับรหัสผ่านสำเร็จเท่านั้น
- อย่าพยายามสร้างแบบมีเงื่อนไขสำหรับขั้นตอนแบบไม่ใช้รหัสผ่าน (เช่น Magic Link, SMS OTP หรือการรวมข้อมูลประจำตัว)
- หากจำเป็นต้องใช้การตรวจสอบสิทธิ์แบบหลายปัจจัย คุณต้องรอจนกว่าปัจจัยทั้งหมดจะสำเร็จก่อนจึงจะเริ่มสร้างแบบมีเงื่อนไขได้
- ตรวจสอบว่าเซสชันผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์และถูกต้องทำงานอยู่ก่อนที่จะส่งคำขอไปยังปลายทางการสร้าง
ขั้นตอนการติดตั้งใช้งาน
1. ยกเลิกการดำเนินการป้อนข้อความอัตโนมัติก่อนหน้า
หากหน้าลงชื่อเข้าใช้ใช้การป้อนข้อมูลในแบบฟอร์มอัตโนมัติ (UI แบบมีเงื่อนไข/Get) คุณต้องยกเลิกการเรียกใช้ Get ของข้อมูลเข้าสู่ระบบที่ใช้งานอยู่เพื่อป้องกันความขัดแย้งของเบราว์เซอร์
- เรียกใช้
abortController.abort()ในAbortControllerที่แนบมากับคำขอป้อนข้อความอัตโนมัติnavigator.credentials.get()ที่รอดำเนินการก่อนเรียกใช้navigator.credentials.create()
2. การตรวจจับฟีเจอร์
ตรวจสอบว่าการสร้างแบบมีเงื่อนไขพร้อมใช้งานหรือไม่โดยตรวจสอบ conditionalCreate ด้วย PublicKeyCredential.getClientCapabilities()
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate) {
// Conditional create is available
}
3. สร้างพาสคีย์ด้วยการสร้างแบบมีเงื่อนไข
- ส่ง
mediation: 'conditional'ภายในตัวเลือกnavigator.credentials.create()ซึ่งจะส่งสัญญาณให้เบราว์เซอร์จัดการขั้นตอนการสร้างพาสคีย์อย่างเงียบๆ ในเบื้องหลังหรือตามบริบทโดยไม่แสดงกล่องโต้ตอบโมดัลที่รบกวน - ป้อน
excludeCredentialsด้วยรหัสข้อมูลเข้าสู่ระบบพาสคีย์ที่มีอยู่ของผู้ใช้เพื่อหลีกเลี่ยงการลงทะเบียนคีย์ที่ซ้ำกัน
4. การจัดการข้อผิดพลาดแบบเงียบ
- รวมข้อความแจ้งการสร้างพาสคีย์ (
navigator.credentials.create) ไว้ในบล็อก try/catch คุณต้องตรวจจับและละเว้นข้อยกเว้นที่แสดงต่อผู้ใช้โดยทั่วไป (InvalidStateError,NotAllowedError,AbortError) โดยไม่แสดง UI ข้อผิดพลาดต่อผู้ใช้
5. การยืนยันการแสดงตนของผู้ใช้ฝั่งเซิร์ฟเวอร์
- ปลายทางการยืนยันฝั่งเซิร์ฟเวอร์ต้องผ่อนปรนข้อกำหนดการแสดงตนของผู้ใช้ (UP) (
requireUserPresence: false) เท่านั้นเมื่อยืนยันข้อมูลเข้าสู่ระบบที่สร้างโดยทริกเกอร์การสร้างแบบมีเงื่อนไข การยืนยันการแสดงตนอย่างเข้มงวดต้องยังคงทำงานอยู่สำหรับการสร้างที่ชัดเจนมาตรฐาน
6. จัดการการยืนยันเซิร์ฟเวอร์ที่ไม่สำเร็จอย่างเหมาะสม
- หาก
navigator.credentials.create()สำเร็จ แต่การดึงข้อมูลการยืนยันเซิร์ฟเวอร์แสดงผลการตอบกลับที่ไม่ถูกต้อง (เช่น การยืนยันลายเซ็นไม่สำเร็จ) ให้เรียกใช้PublicKeyCredential.signalUnknownCredential()เพื่อป้องกันไม่ให้ข้อมูลเข้าสู่ระบบที่ไม่มีเจ้าของค้างอยู่ในผู้ให้บริการพาสคีย์
ตัวอย่างโค้ด
// optionsFetch and registerVerifyFetch are app-defined server endpoint requests
import { optionsFetch, registerVerifyFetch } from './api.js';
async function triggerConditionalCreate(loginAbortController) {
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate !== true) {
return; // Platform does not support conditional creation
}
// 1. Abort any active autofill conditional-get controllers to clear the WebAuthn pipeline
loginAbortController.abort();
// 2. Fetch creation options signaling the backend that this is a conditional request
const creationOptionsJSON = await optionsFetch({ conditional: true });
const publicKey = PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);
let credential;
try {
// 3. Invoke silent credentials creation prompt
credential = await navigator.credentials.create({
publicKey,
mediation: 'conditional' // Silent background creation mediation
});
} catch (e) {
// 4. Silently swallow common WebAuthn browser exceptions
if (['InvalidStateError', 'NotAllowedError', 'AbortError'].includes(e.name)) {
return;
}
console.error('Unexpected conditional create error:', e);
return;
}
// 5. Server verification step using dedicated Try/Catch block
let encodedResponse = credential.toJSON();
try {
const response = await registerVerifyFetch(encodedResponse);
if (!response.ok) {
// If the server verification fails, clean up using Signal API
if (PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId, // RP ID must match the one defined on the server
credentialId: encodedResponse.id
});
}
}
} catch (serverErr) {
console.error('Verification network failure:', serverErr);
if (PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId, // RP ID must match the one defined on the server
credentialId: encodedResponse.id
});
}
}
}
กลยุทธ์สำรอง
ฟีเจอร์สำรองสำหรับการตรวจหาฟีเจอร์พาสคีย์
ติดตั้ง "webauthn-polyfills" และนำเข้าในบริบทเสมอ
พิจารณาว่าตราบใดที่ระบบรองรับ PublicKeyCredential ระบบก็จะรองรับ PublicKeyCredential.getClientCapabilities ด้วย
import 'webauthn-polyfills';
การซิงค์ API ของ Signal สำรอง
WebAuthn Signal API (webauthn-signals) เป็นการเพิ่มประสิทธิภาพแบบต่อเนื่องที่ใช้เพื่อซิงค์เครื่องมือจัดการรหัสผ่านกับสถานะข้อมูลเข้าสู่ระบบของเซิร์ฟเวอร์
- ประสบการณ์สำรอง: จำกัดการเข้าถึงผ่าน
if (PublicKeyCredential.signalUnknownCredential)หากไม่รองรับ ระบบจะข้ามการซิงค์การยืนยันในเบื้องหลังอย่างราบรื่นโดยไม่แสดงข้อยกเว้นของเบราว์เซอร์
Easy JSON Serialization Fallback
ติดตั้ง "webauthn-polyfills" และนำเข้าในบริบทเสมอ
โปรดทราบว่าตราบใดที่ระบบรองรับ PublicKeyCredential ระบบก็จะรองรับ PublicKeyCredential.parseCreationOptionsFromJSON และ PublicKeyCredential.prototype.toJSON ด้วย
import 'webauthn-polyfills';