Khoá truy cập giúp tài khoản người dùng an toàn, đơn giản và dễ sử dụng hơn.
Việc sử dụng khoá truy cập thay vì mật khẩu là một cách hay để các trang web giúp tài khoản người dùng của họ an toàn hơn, đơn giản hơn, dễ sử dụng hơn và không cần mật khẩu. Với khoá truy cập, người dùng có thể đăng nhập vào một trang web hoặc ứng dụng chỉ bằng cách sử dụng vân tay, khuôn mặt hoặc mã PIN của thiết bị.
Bạn phải tạo khoá truy cập, liên kết với một tài khoản người dùng và lưu trữ khoá công khai của khoá truy cập đó trên máy chủ của bạn thì người dùng mới có thể đăng nhập bằng khoá truy cập đó.
Cách thức hoạt động
Người dùng có thể được yêu cầu tạo khoá truy cập trong một trong các trường hợp sau:
- Khi người dùng đăng nhập bằng mật khẩu.
- Khi người dùng đăng nhập bằng khoá truy cập từ một thiết bị khác (nghĩa là
authenticatorAttachment
làcross-platform
). - Trên một trang chuyên biệt cho phép người dùng quản lý mã xác thực của họ.
Để tạo khoá truy cập, bạn cần dùng API WebAuthn.
Quy trình đăng ký khoá truy cập có 4 thành phần như sau:
- 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à các 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.

Bạn có thể thực hiện quy trình thêm khoá truy cập mới vào tài khoản người dùng hiện có như sau:
- Người dùng đăng nhập vào trang web.
- Sau khi đăng nhập, người dùng sẽ yêu cầu tạo một khoá truy cập trên giao diện người dùng, chẳng hạn như bằng cách nhấn nút "Tạo khoá truy cập".
- Giao diện người dùng yêu cầu thông tin từ phần phụ trợ để tạo khoá truy cập, chẳng hạn như thông tin người dùng, thử thách xác thực và mã thông tin xác thực cần loại trừ.
- Giao diện người dùng gọi
navigator.credentials.create()
để tạo khoá truy cập. Lệnh gọi này trả về một lời hứa. - Khoá truy cập được tạo sau khi người dùng cho phép sử dụng phương thức khoá màn hình của thiết bị. Lời hứa được phân giải và thông tin xác thực khoá công khai được trả về giao diện người dùng.
- 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ợ rồi lưu trữ mã xác thực và khoá công khai liên kết với tài khoản người dùng cho các lần xác thực trong tương lai.
Tính tương thích
WebAuthn được hầu hết các trình duyệt hỗ trợ, nhưng vẫn có một số khoảng trống nhỏ. Hãy tham khảo trang Hỗ trợ thiết bị – khoá truy cập.dev để tìm hiểu xem tổ hợp trình duyệt và hệ điều hành nào hỗ trợ việc tạo khoá truy cập.
Tạo khoá truy cập mới
Dưới đây là cách giao diện người dùng hoạt động theo yêu cầu tạo khoá truy cập mới.
Phát hiện tính năng
Trước khi hiển thị nút "Tạo khoá truy cập mới", hãy kiểm tra xem:
- Trình duyệt hỗ trợ WebAuthn.
- Thiết bị hỗ trợ trình xác thực nền tảng (có thể tạo khoá truy cập và xác thực bằng khoá truy cập).
- Trình duyệt hỗ trợ Giao diện người dùng có điều kiện WebAuthn.
// Availability of `window.PublicKeyCredential` means WebAuthn is usable.
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.
// `isConditionalMediationAvailable` means the feature detection is usable.
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
// Check if user verifying platform authenticator is available.
Promise.all([
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),
PublicKeyCredential.isConditionalMediationAvailable(),
]).then(results => {
if (results.every(r => r === true)) {
// Display "Create a new passkey" button
}
});
}
Khoá truy cập sẽ không được hỗ trợ trên trình duyệt này cho đến khi bạn đáp ứng tất cả điều kiện. Nút "Tạo khoá truy cập mới" sẽ không xuất hiện trước thời điểm đó.
Tìm nạp thông tin quan trọng từ phần phụ trợ
Khi người dùng nhấp vào nút, hãy tìm nạp thông tin quan trọng để gọi navigator.credentials.create()
từ phần phụ trợ:
challenge
: Một thử thách do máy chủ tạo trong ArrayBuffer đối với yêu cầu đăng ký này. Đây là yêu cầu bắt buộc nhưng không được dùng trong quá trình đăng ký, trừ phi thực hiện chứng thực – một chủ đề nâng cao không được đề cập ở đây.user.id
: Mã nhận dạng duy nhất của người dùng. Giá trị này phải là một ArrayBuffer không bao gồm thông tin nhận dạng cá nhân, ví dụ: địa chỉ email hoặc tên người dùng. Giá trị 16 byte ngẫu nhiên được tạo cho mỗi tài khoản sẽ hoạt động tốt.user.name
: Trường này phải chứa một giá trị nhận dạng duy nhất của tài khoản mà người dùng sẽ nhận ra, chẳng hạn như địa chỉ email hoặc tên người dùng. Thông tin này sẽ hiển thị trong bộ chọn tài khoản. (Nếu sử dụng tên người dùng, hãy sử dụng cùng một giá trị như trong quy trình xác thực bằng mật khẩu.)user.displayName
: Trường này là tên bắt buộc và thân thiện hơn với người dùng đối với tài khoản. Tên này không nhất thiết phải là tên duy nhất và có thể là tên người dùng đã chọn. Nếu trang web của bạn không có giá trị phù hợp để đưa vào đây, hãy chuyển một chuỗi trống. Thông tin này có thể hiển thị trên bộ chọn tài khoản tuỳ thuộc vào trình duyệt.excludeCredentials
: Ngăn việc đăng ký cùng một thiết bị bằng cách cung cấp danh sách mã xác thực đã đăng ký. Thành phầntransports
, nếu được cung cấp, phải chứa kết quả của lệnh gọigetTransports()
trong quá trình đăng ký từng thông tin xác thực.
Gọi API WebAuthn để tạo khoá truy cập
Gọi navigator.credentials.create()
để tạo khoá truy cập mới. API sẽ trả về một lời hứa hẹn, chờ tương tác của người dùng hiển thị hộp thoại phương thức.
const publicKeyCredentialCreationOptions = {
challenge: *****,
rp: {
name: "Example",
id: "example.com",
},
user: {
id: *****,
name: "john78",
displayName: "John",
},
pubKeyCredParams: [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}],
excludeCredentials: [{
id: *****,
type: 'public-key',
transports: ['internal'],
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: true,
}
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
// Encode and send the credential to the server for verification.
Các thông số không được giải thích ở trên là:
rp.id
: Mã RP là một miền và một trang web có thể chỉ định miền hoặc hậu tố có thể đăng ký. Ví dụ: nếu nguồn gốc của RP làhttps://login.example.com:1337
, thì mã RP có thể làlogin.example.com
hoặcexample.com
. Nếu mã RP được chỉ định làexample.com
, người dùng có thể xác thực trênlogin.example.com
hoặc trên bất kỳ miền con nào trênexample.com
.rp.name
: Tên của bên bị hạn chế.pubKeyCredParams
: Trường này chỉ định thuật toán khoá công khai được hỗ trợ của RP. Bạn nên đặt thành[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]
. Điều này xác định chế độ hỗ trợ cho ECDSA với P-256 và RSA PKCS#1, đồng thời cung cấp mức độ phù hợp đầy đủ.authenticatorSelection.authenticatorAttachment
: Đặt giá trị này thành"platform"
nếu quy trình tạo khoá truy cập này là bản nâng cấp từ một mật khẩu, ví dụ như trong chương trình khuyến mãi sau khi đăng nhập."platform"
cho biết rằng RP muốn có một trình xác thực nền tảng (một trình xác thực được nhúng vào thiết bị nền tảng) sẽ không nhắc chèn (ví dụ: khoá bảo mật USB). Người dùng có một tuỳ chọn đơn giản hơn để tạo khoá truy cập.authenticatorSelection.requireResidentKey
: Đặt giá trị này thành giá trị boolean "true". Thông tin xác thực có thể tìm thấy (khoá thường trú) lưu trữ thông tin người dùng cho khoá truy cập và cho phép người dùng chọn tài khoản khi xác thực.authenticatorSelection.userVerification
: Cho biết phương thức 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"
, tức là trình xác thực có thể bỏ qua bước xác minh người dùng. Hãy đặt giá trị này thành"preferred"
hoặc bỏ qua thuộc tính.
Gửi thông tin xác thực khoá công khai được trả về đến phần phụ trợ
Sau khi người dùng đồng ý sử dụng phương thức khoá màn hình của thiết bị, một khoá truy cập sẽ được tạo và lời hứa sẽ được giải quyết, trả về một đối tượng PublicKeyCredential cho giao diện người dùng.
Lời hứa có thể bị từ chối vì nhiều lý do. Bạn có thể xử lý các lỗi này bằng cách kiểm tra thuộc tính name
của đối tượng Error
:
InvalidStateError
: Khoá truy cập đã tồn tại trên thiết bị. Người dùng sẽ không nhìn thấy hộp thoại lỗi và trang web không nên coi đây là lỗi — người dùng muốn thiết bị cục bộ đã được đăng ký và trang web đã đăng ký như vậy.NotAllowedError
: Người dùng đã huỷ thao tác.- Các trường hợp ngoại lệ khác: Đã xảy ra sự cố không mong muốn. Trình duyệt sẽ hiển thị một hộp thoại lỗi với 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 khoá truy cập đã tạo. Mã này giúp trình duyệt xác định xem thiết bị có khoá truy cập phù hợp hay không khi xác thực. Giá trị này cần được lưu trữ trong cơ sở dữ liệu trên phần phụ trợ.rawId
: Một phiên bản ArrayBuffer của mã thông tin xác thực.response.clientDataJSON
: Dữ liệu ứng dụng được mã hoá bằng ArrayBuffer.response.attestationObject
: Đối tượng chứng thực được mã hoá ArrayBuffer. Tệp này chứa các thông tin quan trọng như mã nhận dạng bên bị hạn chế, cờ và khoá công khai.authenticatorAttachment
: Trả về"platform"
khi thông tin xác thực này được tạo trên một thiết bị có khoá truy cập.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 phần phụ trợ, bạn nên gửi toàn bộ đối tượng đến phần phụ trợ sau khi mã hoá một phần bằng base64url.
Lưu thông tin xác thực
Khi nhận được thông tin xác thực khoá công khai trên phần phụ trợ, hãy truyền thông tin đó vào thư viện FIDO để xử lý đối tượng.
Sau đó, bạn có thể lưu trữ thông tin được truy xuất từ thông tin xác thực vào cơ sở dữ liệu để sử dụng sau này. Danh sách sau đây bao gồm một số thuộc tính điển hình để lưu:
- Mã thông tin xác thực (Khoá chính)
- User ID
- Khoá công khai
Thông tin xác thực khoá công khai cũng bao gồm những thông tin sau mà bạn nên lưu trong cơ sở dữ liệu:
- Cờ Điều kiện sao lưu:
true
nếu thiết bị đủ điều kiện để đồng bộ hoá khoá truy cập. - Cờ trạng thái sao lưu:
true
nếu khoá truy cập đã tạo thực sự được đặt để đồng bộ hoá. - Công cụ di chuyển: Danh sách công cụ di chuyển mà thiết bị hỗ trợ:
"internal"
có nghĩa là thiết bị hỗ trợ khoá truy cập,"hybrid"
có nghĩa là thiết bị này cũng hỗ trợ xác thực trên một thiết bị khác.
Để xác thực người dùng, hãy đọc phần Đăng nhập bằng khoá truy cập thông qua tính năng tự động điền biểu mẫu.