ลงชื่อเข้าใช้ด้วยพาสคีย์ผ่านการป้อนแบบฟอร์มอัตโนมัติ

สร้างประสบการณ์การลงชื่อเข้าใช้ที่ใช้ประโยชน์จากพาสคีย์โดยที่ยังสามารถรองรับผู้ใช้รหัสผ่านที่มีอยู่แล้วได้

พาสคีย์จะแทนที่รหัสผ่าน และทำให้บัญชีผู้ใช้ในเว็บปลอดภัยขึ้น ใช้งานง่ายขึ้น อย่างไรก็ตาม การเปลี่ยนจากการตรวจสอบสิทธิ์แบบใช้รหัสผ่านไปใช้พาสคีย์อาจทำให้ประสบการณ์ของผู้ใช้ยุ่งยากขึ้น การใช้การป้อนข้อความอัตโนมัติในแบบฟอร์มเพื่อแนะนำพาสคีย์จะช่วยสร้างประสบการณ์การใช้งานแบบรวม

เหตุผลที่ควรใช้ฟีเจอร์ป้อนข้อความอัตโนมัติของแบบฟอร์มเพื่อลงชื่อเข้าใช้ด้วยพาสคีย์

เมื่อใช้พาสคีย์ ผู้ใช้จะลงชื่อเข้าใช้เว็บไซต์ได้โดยใช้เพียงลายนิ้วมือ ใบหน้า หรือ PIN ของอุปกรณ์

ตามหลักการแล้ว ผู้ใช้ไม่ควรต้องใช้รหัสผ่าน และขั้นตอนการตรวจสอบสิทธิ์ควรทำได้ง่ายเพียงคลิกปุ่มลงชื่อเข้าใช้ครั้งเดียว เมื่อผู้ใช้แตะปุ่ม กล่องโต้ตอบตัวเลือกบัญชีจะปรากฏขึ้น ผู้ใช้สามารถเลือกบัญชี ปลดล็อกหน้าจอเพื่อยืนยันและลงชื่อเข้าใช้ได้

อย่างไรก็ตาม การเปลี่ยนจากรหัสผ่านไปใช้การตรวจสอบสิทธิ์ด้วยพาสคีย์อาจเป็นเรื่องท้าทาย เมื่อผู้ใช้เปลี่ยนไปใช้พาสคีย์ ก็ยังคงมีผู้ใช้บางส่วนที่ใช้รหัสผ่านอยู่ และเว็บไซต์จะต้องรองรับผู้ใช้ทั้ง 2 ประเภท ไม่ควรคาดหวังให้ผู้ใช้จำเว็บไซต์ที่ตนสลับไปใช้พาสคีย์ ดังนั้นการขอให้ผู้ใช้เลือกวิธีที่จะใช้ล่วงหน้าจึงทำให้ประสบการณ์การใช้งานของผู้ใช้ไม่มีประสิทธิภาพ

นอกจากนี้พาสคีย์ยังเป็นเทคโนโลยีใหม่ การอธิบายและทำให้ผู้ใช้รู้สึกสะดวกสบายเมื่อใช้ฟีเจอร์เหล่านี้อาจเป็นเรื่องท้าทายสำหรับเว็บไซต์ เราสามารถใช้ประสบการณ์การใช้งานที่คุ้นเคยในการป้อนรหัสผ่านอัตโนมัติเพื่อแก้ปัญหาทั้ง 2 ประการ

UI แบบมีเงื่อนไข

หากต้องการสร้างประสบการณ์ของผู้ใช้ที่มีประสิทธิภาพสำหรับทั้งผู้ใช้พาสคีย์และรหัสผ่าน คุณอาจรวมพาสคีย์ไว้ในคำแนะนำการป้อนข้อความอัตโนมัติ การดำเนินการนี้เรียกว่า UI แบบมีเงื่อนไข และเป็นส่วนหนึ่งของมาตรฐาน WebAuthn

ทันทีที่ผู้ใช้แตะช่องป้อนชื่อผู้ใช้ กล่องโต้ตอบคำแนะนำการป้อนข้อความอัตโนมัติจะปรากฏขึ้น ซึ่งจะไฮไลต์พาสคีย์ที่บันทึกไว้พร้อมกับคำแนะนำการป้อนรหัสผ่านอัตโนมัติ จากนั้นผู้ใช้จะเลือกบัญชีและใช้การล็อกหน้าจอของอุปกรณ์เพื่อลงชื่อเข้าใช้ได้

วิธีนี้จะช่วยให้ผู้ใช้ลงชื่อเข้าใช้เว็บไซต์ด้วยแบบฟอร์มที่มีอยู่ได้ราวกับไม่มีอะไรเปลี่ยนแปลง แต่จะมีประโยชน์ด้านความปลอดภัยเพิ่มเติมจากพาสคีย์หากผู้ใช้มี

วิธีการทำงาน

หากต้องการตรวจสอบสิทธิ์ด้วยพาสคีย์ คุณจะใช้ WebAuthn API

คอมโพเนนต์ 4 อย่างในขั้นตอนการตรวจสอบสิทธิ์ด้วยพาสคีย์มีดังนี้ ผู้ใช้

  • แบ็กเอนด์: เซิร์ฟเวอร์แบ็กเอนด์ที่มีฐานข้อมูลบัญชีที่จัดเก็บคีย์สาธารณะและข้อมูลเมตาอื่นๆ เกี่ยวกับพาสคีย์
  • หน้าเว็บ: หน้าเว็บที่สื่อสารกับเบราว์เซอร์และส่งคำขอดึงข้อมูลไปยังแบ็กเอนด์
  • เบราว์เซอร์: เบราว์เซอร์ของผู้ใช้ที่เรียกใช้ JavaScript
  • โปรแกรมตรวจสอบสิทธิ์: โปรแกรมตรวจสอบสิทธิ์ของผู้ใช้ที่สร้างและจัดเก็บพาสคีย์ ซึ่งอาจอยู่ในอุปกรณ์เดียวกับเบราว์เซอร์ (เช่น เมื่อใช้ Windows Hello) หรือในอุปกรณ์อื่น เช่น โทรศัพท์
แผนภาพการตรวจสอบสิทธิ์ด้วยพาสคีย์
  1. ทันทีที่ผู้ใช้ไปที่หน้าเว็บ หน้าเว็บจะขอคำขอยืนยันจากแบ็กเอนด์เพื่อตรวจสอบสิทธิ์ด้วยพาสคีย์ และเรียกใช้ navigator.credentials.get() เพื่อเริ่มตรวจสอบสิทธิ์ด้วยพาสคีย์ ซึ่งจะแสดงผลเป็น Promise
  2. เมื่อผู้ใช้วางเคอร์เซอร์ในช่องลงชื่อเข้าใช้ เบราว์เซอร์จะแสดงกล่องโต้ตอบป้อนรหัสผ่านอัตโนมัติพร้อมพาสคีย์ กล่องโต้ตอบการตรวจสอบสิทธิ์จะปรากฏขึ้นหากผู้ใช้เลือกพาสคีย์
  3. หลังจากผู้ใช้ยืนยันตัวตนโดยใช้การล็อกหน้าจอของอุปกรณ์แล้ว ระบบจะแก้ไขการปฏิเสธและส่งข้อมูลเข้าสู่ระบบคีย์สาธารณะกลับไปยังส่วนหน้า
  4. ฟีดด้านหน้าจะส่งข้อมูลเข้าสู่ระบบคีย์สาธารณะไปยังแบ็กเอนด์ แบ็กเอนด์จะยืนยันลายเซ็นกับคีย์สาธารณะของบัญชีที่ตรงกันในฐานข้อมูล หากดำเนินการสำเร็จ ผู้ใช้จะลงชื่อเข้าใช้

ตรวจสอบสิทธิ์ด้วยพาสคีย์ผ่านการป้อนข้อความอัตโนมัติในแบบฟอร์ม

เมื่อผู้ใช้ต้องการลงชื่อเข้าใช้ คุณสามารถเรียกใช้ WebAuthn get แบบมีเงื่อนไขเพื่อระบุว่าระบบอาจรวมพาสคีย์ไว้ในคำแนะนำการป้อนข้อความอัตโนมัติ การเรียกใช้ navigator.credentials.get() API ของ WebAuthn แบบมีเงื่อนไขจะไม่แสดง UI และรอดำเนินการจนกว่าผู้ใช้จะเลือกบัญชีเพื่อลงชื่อเข้าใช้จากคำแนะนำการป้อนข้อความอัตโนมัติ หากผู้ใช้เลือกพาสคีย์ เบราว์เซอร์จะแก้ไขการยืนยันด้วยข้อมูลเข้าสู่ระบบแทนการกรอกแบบฟอร์มการลงชื่อเข้าใช้ จากนั้นหน้าเว็บมีหน้าที่รับผิดชอบในการลงชื่อเข้าใช้ผู้ใช้

กำกับเนื้อหาในช่องป้อนข้อมูลของแบบฟอร์ม

เพิ่มแอตทริบิวต์ autocomplete ลงในช่องชื่อผู้ใช้ input หากจำเป็น ใส่ username และ webauthn ต่อท้ายเป็นโทเค็นเพื่อให้แอปแนะนำพาสคีย์

<input type="text" name="username" autocomplete="username webauthn" ...>

การตรวจหาฟีเจอร์

ก่อนเรียกใช้การเรียก WebAuthn API แบบมีเงื่อนไข ให้ตรวจสอบดังนี้

  • เบราว์เซอร์รองรับ WebAuthn ด้วย PublicKeyCredential

การรองรับเบราว์เซอร์

  • Chrome: 67
  • Edge: 18.
  • Firefox: 60
  • Safari: 13.

แหล่งที่มา

  • เบราว์เซอร์รองรับ WebAuthn conditional UI ด้วย PublicKeyCredenital.isConditionalMediationAvailable()

การรองรับเบราว์เซอร์

  • Chrome: 108
  • ขอบ: 108
  • Firefox: 119.
  • Safari: 16

แหล่งที่มา

// 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

เรียกชาเลนจ์จากเซิร์ฟเวอร์ RP ที่จำเป็นต้องใช้เพื่อเรียกใช้ navigator.credentials.get():

  • challenge: คำขอยืนยันที่เซิร์ฟเวอร์สร้างขึ้นใน ArrayBuffer เพื่อป้องกันการโจมตีซ้ำ โปรดสร้างคำถามใหม่ทุกครั้งที่พยายามลงชื่อเข้าใช้ และไม่ต้องสนใจการทดสอบหลังจากผ่านไประยะหนึ่งหรือหลังจากที่ตรวจสอบการลงชื่อเข้าใช้แล้วไม่สำเร็จ ให้คิดว่าเป็นโทเค็น CSRF
  • allowCredentials: อาร์เรย์ของข้อมูลเข้าสู่ระบบที่ยอมรับสําหรับการตรวจสอบสิทธิ์นี้ ส่งผ่านอาร์เรย์ว่างเพื่อให้ผู้ใช้เลือกพาสคีย์ที่ใช้ได้จากรายการที่เบราว์เซอร์แสดง
  • userVerification: ระบุว่าการยืนยันผู้ใช้โดยใช้การล็อกหน้าจออุปกรณ์เป็น"required", "preferred" หรือ "discouraged" ค่าเริ่มต้นคือ "preferred" ซึ่งหมายความว่า Authenticator อาจข้ามการยืนยันผู้ใช้ ตั้งค่าเป็น "preferred" หรือไม่ใช้พร็อพเพอร์ตี้นี้

เรียกใช้ WebAuthn API พร้อม Flag conditional เพื่อตรวจสอบสิทธิ์ผู้ใช้

โทร 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 คือโดเมนและเว็บไซต์จะระบุโดเมนหรือคำต่อท้ายที่จดทะเบียนได้ ค่านี้ต้องตรงกับ rp.id ที่ใช้เมื่อสร้างพาสคีย์

อย่าลืมระบุ mediation: 'conditional' เพื่อให้คำขอเป็นแบบมีเงื่อนไข

ส่งข้อมูลเข้าสู่ระบบคีย์สาธารณะที่ได้รับไปยังเซิร์ฟเวอร์ RP

หลังจากผู้ใช้เลือกบัญชีและให้ความยินยอมโดยใช้การล็อกหน้าจอของอุปกรณ์แล้ว ระบบจะแก้ปัญหาตามสัญญาโดยส่งออบเจ็กต์ PublicKeyCredential ไปยังส่วนหน้าของ RP

การปฏิเสธการเสนอราคาอาจเกิดจากหลายสาเหตุ คุณต้องจัดการข้อผิดพลาดตามความเหมาะสม โดยขึ้นอยู่กับพร็อพเพอร์ตี้ name ของออบเจ็กต์ Error ดังนี้

  • NotAllowedError: ผู้ใช้ยกเลิกการดำเนินการ
  • ข้อยกเว้นอื่นๆ: เกิดข้อผิดพลาดที่ไม่คาดคิด เบราว์เซอร์จะแสดงกล่องโต้ตอบแสดงข้อผิดพลาดให้ผู้ใช้เห็น

ออบเจ็กต์ข้อมูลเข้าสู่ระบบด้วยคีย์สาธารณะมีพร็อพเพอร์ตี้ต่อไปนี้

  • id: รหัสที่เข้ารหัส Base64url ของข้อมูลเข้าสู่ระบบพาสคีย์ที่ตรวจสอบสิทธิ์แล้ว
  • rawId: รหัสข้อมูลเข้าสู่ระบบเวอร์ชัน ArrayBuffer
  • response.clientDataJSON: ArrayBuffer ของข้อมูลไคลเอ็นต์ ช่องนี้มีข้อมูล เช่น ภารกิจและต้นทางที่เซิร์ฟเวอร์ RP จะต้องยืนยัน
  • response.authenticatorData: ArrayBuffer ของข้อมูลโปรแกรมตรวจสอบสิทธิ์ ช่องนี้มีข้อมูล เช่น รหัส RP
  • response.signature: ArrayBuffer ของลายเซ็น ค่านี้เป็นหัวใจสำคัญของข้อมูลเข้าสู่ระบบและต้องได้รับการยืนยันในเซิร์ฟเวอร์
  • response.userHandle: ArrayBuffer ที่มีรหัสผู้ใช้ที่ตั้งไว้ ณ เวลาที่สร้างขึ้น คุณสามารถใช้ค่านี้แทนรหัสข้อมูลเข้าสู่ระบบ หากเซิร์ฟเวอร์จำเป็นต้องเลือกค่ารหัสที่ใช้ หรือหากแบ็กเอนด์ต้องการหลีกเลี่ยงการสร้างดัชนีบนรหัสข้อมูลเข้าสู่ระบบ
  • authenticatorAttachment: แสดงผล platform เมื่อข้อมูลเข้าสู่ระบบนี้มาจากอุปกรณ์ในเครื่อง มิฉะนั้น cross-platform โดยเฉพาะอย่างยิ่งเมื่อผู้ใช้ใช้โทรศัพท์เพื่อลงชื่อเข้าใช้ หากผู้ใช้จำเป็นต้องใช้โทรศัพท์เพื่อลงชื่อเข้าใช้ ให้พิจารณาแจ้งให้ผู้ใช้สร้างพาสคีย์ในอุปกรณ์เครื่องนั้น
  • type: ฟิลด์นี้จะตั้งค่าเป็น "public-key" เสมอ

หากคุณใช้ไลบรารีเพื่อจัดการออบเจ็กต์ข้อมูลเข้าสู่ระบบด้วยคีย์สาธารณะในเซิร์ฟเวอร์ RP เราขอแนะนำให้ส่งออบเจ็กต์ทั้งหมดไปยังเซิร์ฟเวอร์หลังจากเข้ารหัสบางส่วนด้วย base64url

ยืนยันลายเซ็น

เมื่อได้รับข้อมูลเข้าสู่ระบบคีย์สาธารณะในเซิร์ฟเวอร์ ให้ส่งข้อมูลดังกล่าวไปยังไลบรารี FIDO เพื่อประมวลผลออบเจ็กต์

ค้นหารหัสข้อมูลเข้าสู่ระบบที่ตรงกันด้วยพร็อพเพอร์ตี้ id (หากต้องการระบุบัญชีผู้ใช้ ให้ใช้พร็อพเพอร์ตี้ userHandle ซึ่งเป็น user.id ที่คุณระบุไว้เมื่อสร้างข้อมูลเข้าสู่ระบบ) ดูว่าsignature ของข้อมูลเข้าสู่ระบบยืนยันได้ด้วยคีย์สาธารณะที่จัดเก็บไว้หรือไม่ เราขอแนะนำให้ใช้ไลบรารีหรือโซลูชันฝั่งเซิร์ฟเวอร์แทนการเขียนโค้ดของคุณเอง คุณสามารถค้นหาไลบรารีโอเพนซอร์สได้ในที่เก็บ GitHub ของ awesome-webauth

เมื่อยืนยันข้อมูลเข้าสู่ระบบด้วยคีย์สาธารณะที่ตรงกันแล้ว ให้ลงชื่อเข้าใช้ผู้ใช้

ทําตามวิธีการโดยละเอียดเพิ่มเติมที่การตรวจสอบสิทธิ์พาสคีย์ฝั่งเซิร์ฟเวอร์

แหล่งข้อมูล