Đăng nhập bằng khoá truy cập thông qua tính năng tự động điền biểu mẫu

Tạo trải nghiệm đăng nhập tận dụng khoá truy cập trong khi vẫn hỗ trợ người dùng hiện tại sử dụng mật khẩu.

Khoá truy cập thay thế mật khẩu và giúp tài khoản người dùng trên web an toàn, đơn giản và dễ sử dụng hơn. Tuy nhiên, việc chuyển đổi từ xác thực dựa trên mật khẩu sang xác thực dựa trên khoá truy cập có thể làm phức tạp trải nghiệm người dùng. Việc sử dụng tính năng tự động điền biểu mẫu để đề xuất khoá truy cập có thể giúp tạo ra trải nghiệm hợp nhất.

Với khoá truy cập, người dùng có thể đăng nhập vào một trang web chỉ bằng cách sử dụng vân tay, khuôn mặt hoặc mã PIN của thiết bị.

Lý tưởng nhất là người dùng không cần mật khẩu và quy trình xác thực có thể đơn giản như một nút đăng nhập duy nhất. Khi người dùng nhấn vào nút này, một hộp thoại bộ chọn tài khoản sẽ bật lên, người dùng có thể chọn một tài khoản, mở khoá màn hình để xác minh và đăng nhập.

Tuy nhiên, việc chuyển đổi từ xác thực bằng mật khẩu sang xác thực dựa trên khoá truy cập có thể khá khó khăn. Khi người dùng chuyển sang khoá truy cập, vẫn sẽ có những người dùng sử dụng mật khẩu và các trang web sẽ cần phải đáp ứng cả hai loại người dùng. Người dùng không nên phải nhớ những trang web mà họ đã chuyển sang khoá truy cập, vì vậy, việc yêu cầu người dùng chọn phương thức để sử dụng trước sẽ gây ra trải nghiệm người dùng kém.

Khoá truy cập cũng là một công nghệ mới. Việc giải thích và đảm bảo người dùng thoải mái sử dụng các tính năng này có thể là một thách thức đối với các trang web. Chúng ta có thể dựa vào trải nghiệm quen thuộc của người dùng về tính năng tự động điền mật khẩu để giải quyết cả hai vấn đề.

Giao diện người dùng có điều kiện

Để tạo trải nghiệm hiệu quả cho người dùng khoá truy cập và mật khẩu, bạn có thể đưa khoá truy cập vào các đề xuất tự động điền. Đây được gọi là giao diện người dùng có điều kiện và là một phần của tiêu chuẩn WebAuthn.

Ngay khi người dùng nhấn vào trường nhập tên người dùng, một hộp thoại đề xuất tự động điền sẽ bật lên, làm nổi bật các khoá truy cập đã lưu cùng với các đề xuất tự động điền mật khẩu. Sau đó, người dùng có thể chọn một tài khoản và sử dụng tính năng khoá màn hình của thiết bị để đăng nhập.

Bằng cách này, người dùng có thể đăng nhập vào trang web của bạn bằng biểu mẫu hiện có như thể không có gì thay đổi, nhưng với lợi ích bảo mật bổ sung của khoá truy cập nếu họ có khoá truy cập.

Cách hoạt động

Để xác thực bằng khoá truy cập, bạn sử dụng API WebAuthn.

Có 4 thành phần trong quy trình xác thực bằng khoá truy cập: người dùng:

  • Phần phụ trợ: Máy chủ phụ trợ lưu trữ cơ sở dữ liệu tài khoản lưu trữ khoá công khai và siêu dữ liệu khác về khoá truy cập.
  • Giao diện người dùng: Giao diện người dùng giao tiếp với trình duyệt và gửi yêu cầu tìm nạp đến phần phụ trợ.
  • Trình duyệt: Trình duyệt của người dùng đang chạy JavaScript.
  • Authenticator: Trình xác thực của người dùng tạo và lưu trữ khoá truy cập. Thiết bị này có thể nằm trên cùng một thiết bị với trình duyệt (ví dụ: khi sử dụng Windows Hello) hoặc trên một thiết bị khác, chẳng hạn như điện thoại.
Sơ đồ xác thực khoá truy cập
  1. Ngay khi người dùng truy cập vào giao diện người dùng, giao diện này sẽ yêu cầu một thử thách từ phần phụ trợ để xác thực bằng khoá truy cập và gọi navigator.credentials.get() để bắt đầu xác thực bằng khoá truy cập. Thao tác này sẽ trả về một Promise.
  2. Khi người dùng đặt con trỏ vào trường đăng nhập, trình duyệt sẽ hiển thị hộp thoại tự động điền mật khẩu, bao gồm cả khoá truy cập. Hộp thoại xác thực sẽ xuất hiện nếu người dùng chọn khoá truy cập.
  3. Sau khi người dùng xác minh danh tính bằng phương thức khoá màn hình của thiết bị, lời hứa sẽ được giải quyết và thông tin xác thực khoá công khai sẽ được trả về cho giao diện người dùng.
  4. Giao diện người dùng gửi thông tin xác thực khoá công khai đến phần phụ trợ. Phần phụ trợ xác minh chữ ký dựa trên khoá công khai của tài khoản đã so khớp trong cơ sở dữ liệu. Nếu thành công, người dùng sẽ được đăng nhập.

Xác thực bằng khoá truy cập thông qua tính năng tự động điền biểu mẫu

Khi người dùng muốn đăng nhập, bạn có thể thực hiện lệnh gọi get WebAuthn có điều kiện để cho biết rằng khoá truy cập có thể được đưa vào các đề xuất tự động điền. Lệnh gọi có điều kiện đến API navigator.credentials.get() của WebAuthn không hiển thị giao diện người dùng và vẫn ở trạng thái chờ cho đến khi người dùng chọn một tài khoản để đăng nhập từ các đề xuất tự động điền. Nếu người dùng chọn một khoá truy cập, trình duyệt sẽ phân giải lời hứa bằng thông tin xác thực thay vì điền vào biểu mẫu đăng nhập. Sau đó, trang có trách nhiệm đăng nhập cho người dùng.

Chú thích trường nhập trên biểu mẫu

Thêm thuộc tính autocomplete vào trường tên người dùng input (nếu cần). Thêm usernamewebauthn dưới dạng mã thông báo để cho phép đề xuất khoá truy cập.

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

Phát hiện tính năng

Trước khi gọi lệnh gọi API WebAuthn có điều kiện, hãy kiểm tra xem:

  • Trình duyệt hỗ trợ WebAuthn bằng PublicKeyCredential.

Hỗ trợ trình duyệt

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

Nguồn

Hỗ trợ trình duyệt

  • Chrome: 108.
  • Edge: 108.
  • Firefox: 119.
  • Safari: 16.

Nguồn

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

Tìm nạp một thử thách từ máy chủ RP

Tìm nạp một thử thách từ máy chủ RP cần thiết để gọi navigator.credentials.get():

  • challenge: Một thử thách do máy chủ tạo trong ArrayBuffer. Điều này là cần thiết để ngăn chặn các cuộc tấn công phát lại. Hãy nhớ tạo một thử thách mới cho mỗi lần đăng nhập và bỏ qua thử thách đó sau một khoảng thời gian nhất định hoặc sau khi một lần đăng nhập không xác thực được. Hãy xem đó như một mã thông báo CSRF.
  • allowCredentials: Một mảng gồm các thông tin xác thực được chấp nhận cho quy trình xác thực này. Truyền một mảng trống để cho phép người dùng chọn một khoá truy cập có sẵn trong danh sách do trình duyệt hiển thị.
  • userVerification: Cho biết liệu quy trình xác minh người dùng bằng phương thức khoá màn hình thiết bị có phải là "required", "preferred" hay "discouraged" hay không. Giá trị mặc định là "preferred", nghĩa là trình xác thực có thể bỏ qua bước xác minh người dùng. Đặt thuộc tính này thành "preferred" hoặc bỏ qua thuộc tính này.

Gọi API WebAuthn bằng cờ conditional để xác thực người dùng

Gọi navigator.credentials.get() để bắt đầu chờ xác thực người dùng.

// 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: Mã nhận dạng RP là một miền và trang web có thể chỉ định miền của nó hoặc một hậu tố có thể đăng ký. Giá trị này phải khớp với rp.id được dùng khi tạo khoá truy cập.

Hãy nhớ chỉ định mediation: 'conditional' để đưa ra yêu cầu có điều kiện.

Gửi thông tin xác thực khoá công khai được trả về đến máy chủ RP

Sau khi người dùng chọn một tài khoản và đồng ý sử dụng phương thức khoá màn hình của thiết bị, lời hứa sẽ được giải quyết bằng cách trả về đối tượng PublicKeyCredential cho giao diện người dùng RP.

Lời hứa có thể bị từ chối vì nhiều lý do. Bạn cần xử lý các lỗi cho phù hợp, tuỳ thuộc vào thuộc tính name của đối tượng Error:

  • NotAllowedError: Người dùng đã huỷ thao tác.
  • Ngoại lệ khác: Đã xảy ra lỗi không mong muốn. Trình duyệt hiển thị hộp thoại lỗi cho người dùng.

Đối tượng thông tin xác thực khoá công khai chứa các thuộc tính sau:

  • id: Mã nhận dạng được mã hoá base64url của thông tin đăng nhập bằng khoá truy cập đã được xác thực.
  • rawId: Phiên bản ArrayBuffer của mã thông tin xác thực.
  • response.clientDataJSON: Một ArrayBuffer của dữ liệu ứng dụng. Trường này chứa thông tin như thử thách và nguồn gốc mà máy chủ RP cần xác minh.
  • response.authenticatorData: Một ArrayBuffer của dữ liệu trình xác thực. Trường này chứa thông tin như mã nhận dạng RP.
  • response.signature: Một ArrayBuffer của chữ ký. Giá trị này là cốt lõi của thông tin xác thực và cần được xác minh trên máy chủ.
  • response.userHandle: Một ArrayBuffer chứa mã nhận dạng người dùng được thiết lập tại thời điểm tạo. Bạn có thể sử dụng giá trị này thay vì mã thông tin xác thực nếu máy chủ cần chọn giá trị mã nhận dạng để sử dụng hoặc nếu phần phụ trợ muốn tránh tạo chỉ mục cho mã thông tin xác thực.
  • authenticatorAttachment: Trả về platform khi thông tin xác thực này đến từ thiết bị cục bộ. Nếu không, cross-platform, đặc biệt là khi người dùng dùng điện thoại để đăng nhập. Nếu người dùng cần sử dụng điện thoại để đăng nhập, hãy cân nhắc nhắc họ tạo khoá truy cập trên thiết bị cục bộ.
  • type: Trường này luôn được đặt thành "public-key".

Nếu sử dụng thư viện để xử lý đối tượng thông tin xác thực khoá công khai trên máy chủ RP, bạn nên gửi toàn bộ đối tượng đó đến máy chủ sau khi mã hoá một phần đối tượng bằng base64url.

Xác minh chữ ký

Khi bạn nhận được thông tin xác thực khoá công khai trên máy chủ, hãy chuyển thông tin đó đến thư viện FIDO để xử lý đối tượng.

Tra cứu mã thông tin xác thực phù hợp bằng thuộc tính id (Nếu bạn cần xác định tài khoản người dùng, hãy sử dụng thuộc tính userHandle, đây là user.id mà bạn đã chỉ định khi tạo thông tin xác thực). Xem liệu có thể xác minh signature của thông tin xác thực bằng khoá công khai đã lưu trữ hay không. Để làm như vậy, bạn nên sử dụng thư viện phía máy chủ hoặc một giải pháp thay vì tự viết mã. Bạn có thể tìm thấy các thư viện nguồn mở trong kho lưu trữ GitHub awesome-webauth.

Sau khi xác minh thông tin xác thực bằng khoá công khai trùng khớp, hãy đăng nhập người dùng.

Hãy làm theo hướng dẫn chi tiết hơn tại bài viết Xác thực khoá truy cập phía máy chủ

Tài nguyên