Tạo một trải nghiệm đăng nhập tận dụng khoá truy cập trong khi vẫn hỗ trợ những người dùng có 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 hơn, đơn giản hơn và dễ sử dụng hơn. Tuy nhiên, việc chuyển đổi từ phương thức xác thực dựa trên mật khẩu sang phương thức xác thực dựa trên khoá truy cập có thể khiến trải nghiệm người dùng trở nên phức tạp. 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 một trải nghiệm hợp nhất.
Tại sao bạn nên sử dụng tính năng tự động điền vào biểu mẫu để đăng nhập bằng khoá truy cập?
Với khoá truy cập, người dùng có thể đăng nhập vào một trang web chỉ bằng vân tay, khuôn mặt hoặc mã PIN của thiết bị.
Lý tưởng nhất là không có người dùng sử dụng mật khẩu và quy trình xác thực có thể chỉ đơn giản như một nút đăng nhập một lần. Khi người dùng nhấn vào nút này, một hộp thoại của 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, quá trình chuyển đổi từ mật khẩu sang xác thực dựa trên khoá truy cập có thể khó khăn. Khi người dùng chuyển sang dùng khoá truy cập, vẫn sẽ có những người sử dụng mật khẩu và các trang web cần đáp ứng cả hai kiểu người dùng này. Người dùng không nên tự ghi 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ẽ mang lại trải nghiệm kém cho người dùng.
Khoá truy cập cũng là một công nghệ mới. Việc giải thích về các nguyên tắc này và đảm bảo người dùng cảm thấy thoải mái khi sử dụng có thể là một thách thức đối với các trang web. Chúng tôi có thể dựa vào trải nghiệm người dùng quen thuộc để tự động điền mật khẩu nhằm giải quyết cả hai vấn đề.
Giao diện người dùng có điều kiện
Để mang lại trải nghiệm hiệu quả cho cả 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, hộp thoại đề xuất tự động điền sẽ bật lên để đánh dấu khoá truy cập đã lưu trữ 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 phương thức khoá màn hình 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 có thêm lợi ích bảo mật bổ sung của khoá truy cập (nếu có).
Cách hoạt động
Để xác thực bằng khoá truy cập, bạn sử dụng API WebAuthn.
4 thành phần trong quy trình xác thực khoá truy cập là: người dùng:
- Phần phụ trợ: Máy chủ phụ trợ lưu giữ cơ sở dữ liệu của 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 của bạn.
- Authenticator: Trình xác thực của người dùng, có chức năng tạo và lưu trữ khoá truy cập. Ứng dụng này có thể ở 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 như điện thoại.
- Ngay khi truy cập vào giao diện người dùng, người dùng sẽ yêu cầu một thử thách từ máy chủ phụ trợ nhằm 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. Hàm này sẽ trả về mộtPromise
. - Khi người dùng đặt con trỏ vào trường đăng nhập, trình duyệt sẽ hiện 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 một khoá truy cập.
- 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 phân giải và thông tin xác thực khoá công khai sẽ được trả về giao diện người dùng.
- Giao diện người dùng sẽ 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 trùng khớp trong cơ sở dữ liệu. Nếu thành công thì người dùng đã đăng nhập được.
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 một 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ờ xử lý cho đến khi người dùng chọn một tài khoản để đăng nhập trong các đề xuất tự động điền. Nếu người dùng chọn khoá truy cập, trình duyệt sẽ thực hiện lời hứa bằng thông tin đăng nhập thay vì điền vào biểu mẫu đăng nhập. Sau đó, trách nhiệm đăng nhập của người dùng thuộc về trang.
Trường nhập chú thích của biểu mẫu
Thêm thuộc tính autocomplete
vào trường input
của tên người dùng nếu cần.
Thêm username
và webauthn
làm mã thông báo để cho phép ứng dụng đề 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 với
PublicKeyCredential
.
- Trình duyệt hỗ trợ Giao diện người dùng có điều kiện WebAuthn với
PublicKeyCredenital.isConditionalMediationAvailable()
.
// 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 thử thách từ máy chủ RP
Tìm nạp lệnh xác thực từ máy chủ RP được yêu cầu để gọi navigator.credentials.get()
:
challenge
: Thử thách do máy chủ tạo trong ArrayBuffer. Đây là yêu cầu bắt buộc để ngăn chặn các cuộc tấn công phát lại. Hãy đảm bảo tạo thử thách mới 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 lần đăng nhập không xác thực được. Hãy coi mã này giống như một mã thông báo CSRF.allowCredentials
: Một loạt thông tin xác thực được chấp nhận cho quá 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ị là"required"
,"preferred"
hay"discouraged"
. Giá trị mặc định là"preferred"
, nghĩa là trình xác thực có thể bỏ qua quy trình xác minh người dùng. Hãy đặt thuộc tính này thành"preferred"
hoặc bỏ qua thuộc tính này.
Gọi API WebAuthn với 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ã RP là một miền và trang web có thể chỉ định miền hoặc hậu tố có thể đăng ký. Giá trị này phải khớp với rp.id được dùng khi khoá truy cập được tạo.
Hãy nhớ chỉ định mediation: 'conditional'
để tạo 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ề tới máy chủ RP
Sau khi người dùng chọn một tài khoản và đồng ý bằng cách sử dụng phương thức khoá màn hình của thiết bị, lời hứa sẽ được phân giải và trả về một đối tượng PublicKeyCredential
về giao diện người dùng RP.
Lời hứa có thể bị từ chối vì một số 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.- Trường hợp ngoại lệ khác: Đã xảy ra lỗi không mong muốn. Trình duyệt sẽ 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 xác thực khoá truy cập đã xác thực.rawId
: Phiên bản ArrayBuffer của mã nhận dạng thông tin xác thực.response.clientDataJSON
: Một ArrayBuffer chứa dữ liệu ứng dụng. Trường này chứa các 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 đặt tại thời điểm tạo. Bạn có thể sử dụng giá trị này thay cho mã nhận dạng thông tin xác thực nếu máy chủ cần chọn các giá trị mã nhận dạng mà máy chủ sử dụng, hoặc nếu phần phụ trợ muốn tránh tạo chỉ mục trên mã nhận dạng 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 thìcross-platform
, đặc biệt là khi người dùng sử 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 việ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 một 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 truyề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 trùng khớp với 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
là user.id
mà bạn đã chỉ định khi tạo thông tin xác thực). Xem liệu signature
của thông tin đăng nhập có thể được xác minh 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 thông tin đăng nhập được xác minh bằng một khoá công khai trùng khớp, hãy đăng nhập cho người dùng.
Làm theo hướng dẫn chi tiết hơn trong phần Xác thực khoá truy cập phía máy chủ