---
url: 'https://www.corbado.com/vi/blog/kiem-thu-e2e-passkeys-playwright-webauthn-trinh-xac-thuc-ao'
title: 'Kiểm thử E2E Passkey với Playwright qua Trình xác thực ảo WebAuthn'
description: 'Tìm hiểu cách thiết lập kiểm thử E2E cho passkey trên các trình duyệt với Playwright, Nightwatch, Selenium và Puppeteer bằng trình xác thực ảo WebAuthn.'
lang: 'vi'
author: 'Anders'
date: '2025-06-17T16:15:42.599Z'
lastModified: '2026-03-25T10:08:13.753Z'
keywords: 'kiểm thử e2e, kiểm thử end-to-end'
category: 'Passkeys Implementation'
---

# Kiểm thử E2E Passkey với Playwright qua Trình xác thực ảo WebAuthn

> **Sứ mệnh của chúng tôi là làm cho Internet trở thành một nơi an toàn hơn**, và tiêu
> chuẩn login mới passkey cung cấp một giải pháp vượt trội để đạt được điều đó. Đó là lý
> do tại sao chúng tôi muốn giúp bạn hiểu rõ hơn về passkey và các đặc điểm của nó.

## 1. Giới thiệu: Kiểm thử E2E Passkey

Passkey ngày càng được chấp nhận rộng rãi như một phương thức xác thực, dựa trên tiêu
chuẩn nền tảng là Web Authentication (WebAuthn). Sự phổ biến của chúng chỉ mới tăng lên
gần đây, điều này làm cho tài liệu và các nguồn thông tin khác tương đối khan hiếm. Điều
này, cùng với bản chất phức tạp của việc triển khai passkey, có thể gây khó khăn cho các
nhà phát triển trong việc tìm kiếm thông tin liên quan về thiết kế, triển khai và đặc biệt
là kiểm thử passkey cho các nền tảng và dịch vụ của họ.

[Hướng dẫn](https://www.corbado.com/vi/blog/ung-dung-crud-react-express-mysql) này nhằm mục đích lấp đầy khoảng
trống đó, tập trung vào các khía cạnh của trình xác thực ảo WebAuthn không được đề cập kỹ
lưỡng trong tài liệu chính thức của nó. Ví dụ, chúng tôi thảo luận về các tùy chọn cấu
hình cho trình xác thực ảo không tự giải thích trong tài liệu, cũng như các giải pháp thay
thế cho một số trường hợp sử dụng mà trình xác thực ảo không cung cấp giải pháp tiện lợi.
Ngoài ra, [hướng dẫn](https://www.corbado.com/vi/blog/ung-dung-crud-react-express-mysql) này cũng hữu ích cho các
nhà phát triển chỉ đơn giản là đang tìm kiếm các ví dụ dễ thực hiện để sử dụng trình xác
thực ảo trong mã kiểm thử.

[Hướng dẫn](https://www.corbado.com/vi/blog/ung-dung-crud-react-express-mysql) của chúng tôi sử dụng các ví dụ từ
[Playwright](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator) để cung
cấp một hướng dẫn đơn giản để kiểm thử hiệu quả việc triển khai passkey trong dự án của
bạn. [Playwright](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator) là
một framework kiểm thử end-to-end (E2E) sử dụng Giao thức Công cụ dành cho nhà phát triển
của Chrome (CDP) làm giao thức tự động hóa trình duyệt. Nếu bạn đang tìm kiếm cụ thể các
ví dụ kỹ thuật về kiểm thử passkey trong
[Playwright](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator), bạn có
thể chuyển thẳng đến Phần 5. Mặt khác, nếu bạn đang sử dụng các framework kiểm thử E2E
khác như [Puppeteer](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator)
hoặc [Selenium](https://www.corbado.com/blog/selenium-passkeys-testing-nodejs) và muốn kiểm thử passkey trên các
framework này, việc triển khai mã kiểm thử sẽ giống hệt hoặc rất giống với các ví dụ được
cung cấp trong hướng dẫn này, tùy thuộc vào framework bạn đang sử dụng. Trong phần tiếp
theo, chúng tôi sẽ cung cấp thông tin nền tảng về các framework kiểm thử E2E khác nhau và
mức độ liên quan của hướng dẫn này đối với các framework đó.

## 2. Bối cảnh: Tự động hóa trình duyệt và các Framework kiểm thử E2E

### 2.1. Tự động hóa trình duyệt là gì?

Tự động hóa trình duyệt, như tên gọi, là quá trình tự động hóa các hành động lặp đi lặp
lại của người dùng trên trình duyệt cho mục đích thu thập dữ liệu web hoặc trong trường
hợp của chúng ta là kiểm thử các ứng dụng web. WebDriver và Giao thức Công cụ dành cho nhà
phát triển của Chrome (CDP) là hai trong số các giao thức tự động hóa trình duyệt chính có
liên quan đến hướng dẫn này, vì mỗi giao thức đều cung cấp một triển khai của trình xác
thực ảo WebAuthn.

### 2.2. WebDriver là gì?

WebDriver là một giao diện điều khiển từ xa có thể được xem như một người trung gian trong
giao tiếp giữa client và trình duyệt. Trọng tâm của giao thức này là cung cấp một giao
diện trung lập về nền tảng và ngôn ngữ, hỗ trợ tất cả các trình duyệt chính, bao gồm cả
những trình duyệt không dựa trên Chromium như Firefox và Safari. Vì giao diện WebDriver
cần quản lý kết nối với client cũng như với trình duyệt, cách tiếp cận này hy sinh tốc độ
và sự ổn định để đổi lấy phạm vi hỗ trợ trình duyệt rộng hơn (tức là độ không ổn định cao
hơn). Các client WebDriver đáng chú ý bao gồm
[Selenium](https://www.corbado.com/blog/selenium-passkeys-testing-nodejs) và
[Nightwatch](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator).

![Passkeys Playwright WebDriver Client](https://www.corbado.com/website-assets/66090bb89296b283f07f69eb_passkeys_playwright_webdriver_client_1efc6dec0e.jpg)

Nguồn: jankaritech

### 2.3. Giao thức Công cụ dành cho nhà phát triển của Chrome (CDP) là gì?

Mặt khác, Giao thức Công cụ dành cho nhà phát triển của Chrome (CDP) không có người trung
gian như giao diện WebDriver giữa client và trình duyệt. Ngoài ra, giao tiếp giữa client
và trình duyệt diễn ra thông qua kết nối socket, trái ngược với kết nối HTTP chậm hơn giữa
client và giao diện WebDriver trong cách tiếp cận trước đó. Những điểm này làm cho CDP
nhanh hơn và ít không ổn định hơn WebDriver. Nhược điểm là giao thức này chỉ được hỗ trợ
cho các trình duyệt dựa trên Chromium như Chrome và Edge. Playwright và
[Puppeteer](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator) là các
client ví dụ sử dụng CDP để giao tiếp với trình duyệt.

![Passkeys PlayWright CDP Client](https://www.corbado.com/website-assets/66090be6820f38fba3e156f3_passkeys_playwright_cdp_client_44e000d341.jpg)

Nguồn: jankaritech

### 2.4. Puppeteer & Playwright là các Framework kiểm thử E2E dựa trên CDP

[Puppeteer](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator), tương
tự như Playwright, là một framework E2E được xây dựng trực tiếp trên CDP. Điều này có
nghĩa là cả Puppeteer và Playwright đều sử dụng cùng một triển khai của trình xác thực ảo
WebAuthn và giao tiếp API sử dụng trình xác thực ảo WebAuthn qua kết nối socket cũng giống
hệt nhau.

Để minh họa, chúng tôi so sánh mã kiểm thử trong cả Playwright và Puppeteer để gọi phương
thức getCredentials, phương thức này trả về danh sách tất cả các thông tin xác thực đã
được đăng ký vào trình xác thực ảo cho đến nay. Chúng tôi cũng đính kèm một trình lắng
nghe sự kiện đơn giản cho sự kiện credentialAdded được kích hoạt khi một thông tin xác
thực passkey được đăng ký thành công. Đừng lo lắng về các chi tiết của việc triển khai, vì
chúng sẽ được giải thích trong các phần sau. Những ví dụ này chỉ đơn giản là để chứng minh
sự tương đồng trong việc triển khai giữa hai framework.

**Playwright:**

```js
const client = await page.context().newCDPSession(page);
await client.send('WebAuthn.enable');
const authenticatorId = const result = await client.send('WebAuthn.addVirtualAuthenticator', { options });

...

// get all credentials registered in the virtual authenticator
const result = await client.send('WebAuthn.getCredentials', { authenticatorId });
console.log(result.credentials);

// add a listener to the credentialAdded event to output a log to the console whenever a passkey credential is registered
client.on('WebAuthn.credentialAdded', () => {
  console.log('Credential Added!');
});
```

**Puppeteer:**

```js
const client = await page.target().createCDPSession();
await client.send('WebAuthn.enable');
const authenticatorId = const result = await client.send('WebAuthn.addVirtualAuthenticator', { options });

...

// get all credentials registered in the virtual authenticator
const result = await client.send('WebAuthn.getCredentials', { authenticatorId });
console.log(result.credentials);

// add a listener to the credentialAdded event to output a log to the console whenever a passkey credential is registered
client.on('WebAuthn.credentialAdded', () => {
  console.log('Credential Added!');
});
```

Mặc dù các phương thức để khởi tạo phiên CDP ở đầu mã kiểm thử có một chút khác biệt, việc
gọi các phương thức và xử lý các sự kiện trong API trình xác thực ảo WebAuthn của CDP là
giống hệt nhau. Điều này có nghĩa là nếu bạn đang muốn sử dụng trình xác thực ảo WebAuthn
trong Puppeteer, bạn có thể theo dõi hướng dẫn này từng dòng một.

### 2.5. Selenium & Nightwatch là các Framework kiểm thử E2E dựa trên WebDriver

[Selenium](https://www.corbado.com/blog/selenium-passkeys-testing-nodejs) và
[Nightwatch](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator) là các
framework kiểm thử E2E dựa vào WebDriver để tự động hóa trình duyệt. Mặc dù việc triển
khai trình xác thực ảo WebAuthn cho WebDriver là riêng biệt so với việc triển khai cho
CDP, các thông số kỹ thuật API của chúng tương tự nhau. Đối với hầu hết mọi phương thức
trong API trình xác thực ảo WebAuthn của CDP, bạn có thể tìm thấy một phương thức tương
ứng trong API trình xác thực ảo WebAuthn của WebDriver. Tuy nhiên, một điều cần lưu ý là
mặc dù có thể đính kèm các trình lắng nghe sự kiện khi một passkey được thêm hoặc xác nhận
thành công trong API trình xác thực ảo WebAuthn của CDP, điều này không thể thực hiện được
trong phiên bản WebDriver.

**Selenium:**

```js
const driver = await new Builder().forBrowser('chrome').build();
const options = new VirtualAuthenticatorOptions();
await driver.addVirtualAuthenticator(options);

...

// get all credentials registered in the virtual authenticator
const credentials = await driver.getCredentials();
```

Rõ ràng là cú pháp thiết lập phiên bản trình xác thực ảo và thực hiện các cuộc gọi API
khác với việc triển khai CDP tương ứng. Tuy nhiên, vì các thông số kỹ thuật API của hai
trình xác thực ảo WebAuthn rất giống nhau, bạn hoàn toàn có thể theo dõi hướng dẫn này để
viết một triển khai tương ứng trên một framework kiểm thử E2E dựa trên WebDriver.

### 2.6. Cypress là Framework kiểm thử E2E với Scripting gốc

[Cypress](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator) là một
framework kiểm thử E2E không được xây dựng chủ yếu trên WebDriver hoặc CDP như các
framework đã đề cập ở trên. Nó sử dụng JavaScript gốc để giao tiếp với trình duyệt. Tuy
nhiên, nó cung cấp quyền truy cập cấp thấp vào CDP, điều đó có nghĩa là có thể gửi các
lệnh CDP thô để sử dụng trình xác thực ảo WebAuthn của CDP.

Bởi vì cú pháp cho quyền truy cập cấp thấp này khá rườm rà và rất khác so với các ví dụ
trên, chúng tôi sẽ không đi vào chi tiết trong hướng dẫn này. Tuy nhiên, thông tin thêm về
cách gọi các lệnh CDP trong
[Cypress](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator) được giải
thích trong hướng dẫn này. Các khái niệm tổng quan về việc sử dụng trình xác thực ảo
WebAuthn của CDP được trình bày trong hướng dẫn này vẫn phù hợp cho những ai muốn kiểm thử
passkey trên
[Cypress](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator).

## 3. Điều gì khiến việc kiểm thử E2E Passkey với Playwright trở thành một vấn đề?

Có nhiều lý do tại sao việc kiểm thử triển khai passkey tự nhiên lại khó khăn hơn so với
các hành động người dùng đơn giản khác trong môi trường web. Nhu cầu xử lý các tương tác
người dùng động liên quan đến
[xác thực sinh trắc học](https://www.corbado.com/vi/faq/ngan-hang-cung-cap-passkey), chẳng hạn như quét vân tay
hoặc nhận dạng khuôn mặt, thêm một lớp phức tạp có thể không thực tế để giải quyết chi
tiết khi viết kiểm thử. Vì [bảo mật](https://www.corbado.com/vi/blog/cach-bat-passkey-tren-android) tự nhiên là
một mối quan tâm lớn trong bối cảnh xác thực, cũng cần phải đảm bảo rằng
[xác thực passkey](https://www.corbado.com/vi/blog/nha-cung-cap-passkey) được tích hợp liền mạch trên các trình
duyệt và thiết bị khác nhau mà không có chỗ cho các lỗ hổng
[bảo mật](https://www.corbado.com/vi/blog/cach-bat-passkey-tren-android).

## 4. Trình xác thực ảo WebAuthn giúp việc kiểm thử E2E Passkey trở nên khả thi

Việc đơn giản hóa sự phức tạp của việc xử lý các tương tác người dùng động liên quan đến
các hoạt động passkey, cũng như kiểm thử sự tích hợp của nó vào các trình duyệt và thiết
bị khác nhau, trở nên dễ dàng hơn bằng cách sử dụng trình xác thực ảo WebAuthn.

### 4.1. Trình xác thực ảo WebAuthn là gì?

Trình xác thực ảo WebAuthn là một biểu diễn phần mềm của mô hình trình xác thực được chỉ
định trong tiêu chuẩn WebAuthn. Nó mô phỏng hành vi của một thiết bị trình xác thực vật
lý, chẳng hạn như
[khóa bảo mật phần cứng](https://www.corbado.com/vi/blog/khoa-bao-mat-phan-cung-fido2-tot-nhat-2025) (ví dụ:
[YubiKey](https://www.corbado.com/glossary/yubikey)) hoặc máy quét
[sinh trắc học](https://www.corbado.com/vi/blog/sinh-trac-hoc-nhan-thuc-nguoi-thanh-toan) (ví dụ: được sử dụng
trong [Face ID](https://www.corbado.com/faq/is-face-id-passkey), Touch ID hoặc
[Windows Hello](https://www.corbado.com/glossary/windows-hello)), nhưng hoạt động hoàn toàn trong phần mềm (vì
vậy không có xác thực vật lý hoặc quét
[sinh trắc học](https://www.corbado.com/vi/blog/sinh-trac-hoc-nhan-thuc-nguoi-thanh-toan) nào liên quan).

### 4.2. Lợi ích của Trình xác thực ảo WebAuthn là gì?

Có hai lợi ích chính của trình xác thực ảo WebAuthn.

#### 4.2.1. Kiểm thử tự động với Trình xác thực ảo WebAuthn

Vì WebDriver và CDP là các công cụ tự động hóa trình duyệt, rõ ràng trường hợp sử dụng
chính của việc triển khai trình xác thực ảo WebAuthn trong các giao thức này là kiểm thử
tự động. Tận dụng các giao thức này, trình xác thực ảo cho phép kiểm thử đơn giản nhưng
toàn diện các chức năng của passkey trong các môi trường được kiểm soát như các framework
kiểm thử E2E (ví dụ: Playwright, Cypress,
[Nightwatch](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator)).

#### 4.2.2. Kiểm thử thủ công và trình diễn với Trình xác thực ảo WebAuthn

Trình xác thực ảo WebAuthn của CDP cũng có thể truy cập được thông qua DevTools của trình
duyệt Chrome, và nó có thể được sử dụng để kiểm thử thủ công hoặc đơn giản là cho mục đích
trình diễn. Với tính năng này, bạn có thể mô phỏng việc
[nhập passkey](https://www.corbado.com/vi/blog/giao-thuc-trao-doi-thong-tin-xac-thuc-cxp-dinh-dang-cxf) trên một
thiết bị không hỗ trợ passkey nguyên bản. Tương ứng, cũng có thể mô phỏng một môi trường
không hỗ trợ passkey trên một thiết bị có hỗ trợ passkey.

![Passkeys Playwright Virtual WebAuthn Authenticator](https://www.corbado.com/website-assets/66090c090678d8241d6e9f67_passkeys_playwright_virtual_webauthn_authenticator_3d1fa5c566.jpg)

Ảnh chụp màn hình ở trên cho thấy một ví dụ về việc sử dụng trình xác thực ảo trong Chrome
cho mục đích kiểm thử thủ công hoặc trình diễn. Bạn có thể thấy rằng có thể có các tùy
chọn cấu hình khác nhau cho trình xác thực ảo, và việc thêm và xóa thông tin xác thực cũng
có thể được theo dõi. Tham khảo hướng dẫn này từ Google để biết thêm thông tin về việc sử
dụng trình xác thực ảo trong trình duyệt của bạn, bao gồm các tùy chọn cấu hình và các giá
trị được đề xuất cho mỗi tùy chọn.

### 4.3. Nhược điểm của Trình xác thực ảo WebAuthn là gì?

Trong khi trình xác thực ảo WebAuthn là một giải pháp thanh lịch để kiểm thử việc triển
khai passkey, có một vài nhược điểm đáng chú ý.

#### 4.3.1. Không thể mô phỏng các chức năng dành riêng cho phần cứng

Là một giải pháp hoàn toàn dựa trên phần mềm, trình xác thực ảo WebAuthn không thể sao
chép các đặc điểm phần cứng độc đáo và các tính năng
[bảo mật](https://www.corbado.com/vi/blog/cach-bat-passkey-tren-android) của các trình xác thực vật lý. Sự khác
biệt giữa việc sử dụng các trình xác thực nền tảng khác nhau (được tích hợp vào một thiết
bị, chẳng hạn như máy quét
[sinh trắc học](https://www.corbado.com/vi/blog/sinh-trac-hoc-nhan-thuc-nguoi-thanh-toan) trên điện thoại thông
minh) và các trình xác thực đa nền tảng khác nhau (là các thiết bị bên ngoài, như
[khóa bảo mật](https://www.corbado.com/vi/glossary/ctap) phần cứng) không thể được mô phỏng bằng trình xác thực
ảo WebAuthn. Mặc dù việc đơn giản hóa hộp đen các sự phức tạp liên quan đến các loại trình
xác thực nền tảng và đa nền tảng khác nhau là một trong những lợi thế của việc sử dụng
trình xác thực ảo WebAuthn, nếu bạn tìm cách mô phỏng và kiểm thử các sắc thái của các
loại trình xác thực khác nhau, các giải pháp khác nên được khám phá.

#### 4.3.2. Tài liệu thưa thớt và các vấn đề kỹ thuật chưa được giải quyết

Do việc áp dụng WebAuthn tương đối gần đây và sự mới mẻ của công nghệ passkey, hệ sinh
thái xung quanh các trình xác thực ảo vẫn đang trong giai đoạn phát triển. Điều này dẫn
đến sự khan hiếm tài liệu toàn diện và các thách thức kỹ thuật chưa được giải quyết, đặc
biệt là trong bối cảnh tích hợp các trình xác thực ảo với các framework kiểm thử tự động.
Hướng dẫn này nhằm giải quyết vấn đề này bằng cách cung cấp những hiểu biết toàn diện về
việc kiểm thử passkey trong môi trường kiểm thử tự động, đồng thời tập trung vào việc giải
quyết những bất tiện vẫn còn tồn tại khi sử dụng các công cụ này và trình bày các giải
pháp thay thế cho những vấn đề đó.

## 5. Cách thiết lập Trình xác thực ảo WebAuthn trong Playwright

Sau khi cài đặt thành công Playwright và các phụ thuộc của nó, bạn có thể bắt đầu viết bài
kiểm thử đầu tiên của mình ngay lập tức bằng cách tạo một tệp có tên kết thúc bằng
.spec.ts hoặc .test.ts với nội dung sau:

```js
import { test, expect } from "@playwright/test";

test("my first test", async ({ page }) => {
    await page.goto("https://passkeys.eu");

    // start simulating user actions
});
```

Để sử dụng trình xác thực ảo WebAuthn trong Playwright, chỉ cần khởi tạo một phiên CDP và
đính kèm một trình xác thực ảo vào đầu một trường hợp kiểm thử, như sau:

```js
test('signup with passkey', async ({ page }) => {
  // Initialize a CDP session for the current page
  const client = await page.context().newCDPSession(page);

  // Enable WebAuthn environment in this session
  await client.send('WebAuthn.enable');

  // Attach a virtual authenticator with specific options
  const result = await client.send('WebAuthn.addVirtualAuthenticator', {
    options: {
      protocol: 'ctap2',
      transport: 'internal',
      hasResidentKey: true,
      hasUserVerification: true,
      isUserVerified: true,
      automaticPresenceSimulation: false,
    },
  });
  const authenticatorId = result.authenticatorId;

  // Further test steps to simulate user interactions and assertions
...
});
```

Các tùy chọn để cấu hình trình xác thực ảo WebAuthn:

- **protocol:** Tùy chọn này chỉ định giao thức mà trình xác thực ảo sử dụng. Các giá trị
  có thể là "ctap2" và "u2f"
- **transport:** Tùy chọn này chỉ định loại trình xác thực mà trình xác thực ảo mô phỏng.
  Các giá trị có thể là "usb", "nfc", "ble", và "internal". Nếu được đặt thành "internal",
  nó sẽ mô phỏng một trình xác thực nền tảng, trong khi các giá trị khác mô phỏng các
  trình xác thực đa nền tảng.
- **hasResidentKey:** Đặt thành true hỗ trợ [Resident Key](https://www.corbado.com/vi/glossary/ctap) (tức là
  thông tin xác thực có thể khám phá phía client).
- **hasUserVerification:** Đặt thành true hỗ trợ Xác minh người dùng. Đặt tùy chọn này
  thành true được khuyến nghị vì nó cho phép mô phỏng việc
  [nhập passkey](https://www.corbado.com/vi/blog/giao-thuc-trao-doi-thong-tin-xac-thuc-cxp-dinh-dang-cxf) thành
  công và thất bại.
- **isUserVerified:** Đặt thành true mô phỏng một kịch bản xác thực thành công, trong khi
  false mô phỏng một lỗi xác thực, chẳng hạn như khi người dùng hủy bỏ việc
  [nhập passkey](https://www.corbado.com/vi/blog/giao-thuc-trao-doi-thong-tin-xac-thuc-cxp-dinh-dang-cxf). Lưu ý
  rằng cài đặt này chỉ có hiệu lực khi hasUserVerification được đặt thành true.
- **automaticPresenceSimulation:** Khi được đặt thành true, việc nhập passkey xảy ra tự
  động và ngay lập tức khi có bất kỳ lời nhắc xác thực nào. Ngược lại, đặt thành false yêu
  cầu khởi tạo thủ công mô phỏng [xác thực passkey](https://www.corbado.com/vi/blog/nha-cung-cap-passkey) trong
  mã kiểm thử. Lựa chọn mô phỏng thủ công (false) được khuyến nghị vì hai lý do:
    - **Tăng khả năng đọc của mã kiểm thử:** Mô phỏng tự động có thể làm che khuất sự hiểu
      biết về luồng kiểm thử, vì các nỗ lực xác thực được mô phỏng mà không có trình kích
      hoạt rõ ràng trong mã kiểm thử.
    - **Tránh hành vi không mong muốn:** Mô phỏng tự động có nghĩa là việc nhập passkey sẽ
      được kích hoạt ngay cả khi người kiểm thử không biết rằng passkey đã được nhắc. Điều
      này đặc biệt là một vấn đề đối với Giao diện người dùng có điều kiện (Conditional
      UI) mà người kiểm thử sẽ dễ dàng bỏ qua hơn.

## 6. Các trường hợp sử dụng Trình xác thực ảo WebAuthn

Trong phần này, chúng tôi khám phá việc sử dụng các phương thức và sự kiện của trình xác
thực ảo WebAuthn trong bối cảnh của cả các trường hợp sử dụng phổ biến và ít gặp.

### 6.1. Cách mô phỏng nỗ lực nhập Passkey trong Playwright

Đây có thể là nhiệm vụ quan trọng nhất nhưng cũng khó hiểu nhất khi sử dụng trình xác thực
ảo WebAuthn trong mã kiểm thử, vì không có phương thức tích hợp rõ ràng nào để kích hoạt
việc nhập passkey. Giải pháp nằm ở các tùy chọn cấu hình của trình xác thực ảo WebAuthn,
cụ thể là isUserVerified và automaticPresenceSimulation. Với các tùy chọn này, chúng ta có
thể mô phỏng tương tác người dùng này thông qua hai cách tiếp cận khác nhau được mô tả
dưới đây.

#### 6.1.1. Cách tiếp cận 1: Mô phỏng tự động với automaticPresenceSimulation được đặt thành true

**Trường hợp 1: Mô phỏng việc nhập passkey thành công**

```js
test('signup with passkey', async ({ page }) => {
...

  await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in');
  await page.getByRole('button', { name: 'Login' }).click();
  // successful passkey input is automatically simulated (isUserVerified=true)
  await expect(page.getByRole('heading', { level: 1 })).toHaveText('Welcome!');

...
});
```

Mô phỏng việc nhập passkey thành công thường không yêu cầu thêm dòng nào trong mã kiểm
thử. Dòng cuối cùng (await expect...) chờ cho trang thay đổi (được kích hoạt bởi việc nhập
passkey thành công ngầm định).

**Trường hợp 2: Mô phỏng việc nhập passkey bị hủy (không kích hoạt thay đổi trong giao
diện người dùng)**

Kiểm thử việc nhập passkey thất bại hoặc bị hủy phức tạp hơn vì nó có thể không dẫn đến
bất kỳ thay đổi nào có thể quan sát được trong giao diện người dùng. Nói cách khác, việc
chờ trang thay đổi như trong ví dụ trước là không đủ để đảm bảo rằng việc nhập passkey đã
hoàn tất xử lý. Việc kiểm tra rằng trang không thay đổi sau khi nhập passkey ngầm định là
vô nghĩa, vì việc kiểm tra gần như chắc chắn sẽ xảy ra trước khi việc nhập passkey hoàn
tất xử lý. Mặc dù trình xác thực ảo cung cấp một cách để chờ một lần nhập passkey thành
công được xử lý bằng cách lắng nghe phát ra sự kiện (như sẽ được thảo luận trong cách tiếp
cận 2), hiện tại không có cách tích hợp nào để phát hiện một lần nhập passkey thất bại
hoặc bị hủy. Một giải pháp thay thế là chỉ cần thêm một thời gian chờ cố định để chờ hoạt
động passkey hoàn tất trước khi kiểm tra rằng giao diện người dùng thực sự vẫn giữ nguyên.

```js
test('signup with passkey', async ({ page }) => {
  // Simulate a set of user actions to trigger a passkey prompt
...

  await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in');

  // Simulate passkey input when prompted in the test
  await inputPasskey(async () => {
    await page.waitForTimeout(300);
    await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in');
  });

  // Further test steps
...
});
```

Trong cả hai trường hợp, khả năng đọc của mã kiểm thử bị hạn chế bởi tính ngầm định của
hoạt động passkey. Như đã đề cập trước đó, cũng sẽ dễ dàng bỏ qua khi Giao diện người dùng
có điều kiện (Conditional UI) có thể được nhắc, trong trường hợp đó hoạt động passkey sẽ
tự động hoàn thành mà người kiểm thử không biết.

#### 6.1.2. Cách tiếp cận 2: Mô phỏng thủ công với automaticPresenceSimulation được đặt thành false

Kích hoạt thủ công việc nhập passkey bằng cách chuyển đổi giá trị của tùy chọn
automaticPresenceSimulation giải quyết các vấn đề gặp phải trong cách tiếp cận trước đó,
cụ thể là về khả năng đọc của mã kiểm thử.

**Trường hợp 1: Mô phỏng việc nhập passkey thành công**

Các đoạn mã sau mô phỏng việc nhập passkey thành công:

```js
async simulateSuccessfulPasskeyInput(operationTrigger: () => Promise<void>) {
  // initialize event listeners to wait for a successful passkey input event
  const operationCompleted = new Promise<void>(resolve => {
    client.on('WebAuthn.credentialAdded', () => resolve());
    client.on('WebAuthn.credentialAsserted', () => resolve());
  });

  // set isUserVerified option to true
  // (so that subsequent passkey operations will be successful)
  await client.send('WebAuthn.setUserVerified', {
    authenticatorId: authenticatorId,
    isUserVerified: true,
  });

  // set automaticPresenceSimulation option to true
  // (so that the virtual authenticator will respond to the next passkey prompt)
  await client.send('WebAuthn.setAutomaticPresenceSimulation', {
    authenticatorId: authenticatorId,
            enabled: true,
  });

  // perform a user action that triggers passkey prompt
  await operationTrigger();

  // wait to receive the event that the passkey was successfully registered or verified
  await operationCompleted;

  // set automaticPresenceSimulation option back to false
  await client.send('WebAuthn.setAutomaticPresenceSimulation', {
    authenticatorId,
            enabled: false,
  });
}
```

```js
test('signup with passkey', async ({ page }) => {
...

  // Simulate passkey input with a promise that triggers a passkey prompt as the argument
  await simulateSuccessfulPasskeyInput(() =>
          page.getByRole('button', { name: 'Create account with passkeys' }).click()
  );

...
});
```

Hàm trợ giúp có thể khá đáng sợ khi bạn nhìn vào nó lần đầu tiên. Sẽ hữu ích khi hiểu rằng
tất cả các sự phức tạp kỹ thuật của việc mô phỏng một hoạt động passkey đều được trừu
tượng hóa vào hàm trợ giúp. Điều này có nghĩa là khi nó được sử dụng bên trong mã kiểm
thử, nó làm cho mã trở nên đơn giản và rõ ràng, như có thể thấy trong đoạn mã thứ hai ở
trên.

So với cách tiếp cận ngầm định trong Mục 6.1.1, cách tiếp cận rõ ràng này cũng làm tăng
khả năng đọc của mã. Điều này sẽ đặc biệt hữu ích khi Giao diện người dùng có điều kiện
(Conditional UI) được nhắc, vì cách tiếp cận rõ ràng này ngăn chặn việc hoàn thành hoạt
động passkey một cách vô ý, ngầm định mà nhà phát triển không biết.

Bây giờ chúng ta hãy hiểu từng phần của hàm trợ giúp.

Đầu tiên, chúng ta định nghĩa promise operationCompleted, nó chờ đợi sự kiện
WebAuthn.credentialAdded hoặc sự kiện WebAuthn.credentialAsserted, như tên gọi, được phát
ra khi một thông tin [xác thực passkey](https://www.corbado.com/vi/blog/nha-cung-cap-passkey) được đăng ký hoặc
xác minh tương ứng. Promise này sẽ được sử dụng sau.

Tiếp theo, tùy chọn isUserVerified được đặt thành true, để hoạt động passkey tiếp theo của
trình xác thực ảo WebAuthn sẽ thành công. automaticPresenceSimulation cũng được đặt thành
true, để trình xác thực ảo WebAuthn sẽ phản hồi
[lời nhắc passkey](https://www.corbado.com/vi/blog/thuc-hanh-tot-nhat-tao-passkey) tiếp theo từ trang web.

Việc chờ promise operationTrigger là cần thiết để tránh tình trạng tranh chấp (race
condition). Tình trạng tranh chấp xảy ra khi trang web nhắc passkey trước khi
automaticPresenceSimulation được đặt thành true. Để ngăn chặn điều này, hành động của
người dùng kích hoạt [lời nhắc passkey](https://www.corbado.com/vi/blog/thuc-hanh-tot-nhat-tao-passkey) phải được
thực hiện sau khi automaticPresenceSimulation được đặt thành true. Trong ví dụ trên, người
dùng nhấp vào nút có tên Create account with passkeys để kích hoạt
[lời nhắc passkey](https://www.corbado.com/vi/blog/thuc-hanh-tot-nhat-tao-passkey).

Sau khi hành động của người dùng hoàn tất, chúng ta phải chờ cho hoạt động passkey thành
công hoàn tất. Điều này được thực hiện bằng cách chờ promise mà chúng ta đã định nghĩa ở
đầu hàm trợ giúp. Việc hoàn thành hoạt động passkey thành công được đánh dấu bằng việc
phát ra sự kiện WebAuthn.credentialAdded hoặc WebAuthn.credentialAsserted. Trong ví dụ
trên, vì người dùng đang đăng ký một passkey, sự kiện WebAuthn.credentialAdded sẽ được
phát ra.

Cuối cùng, tùy chọn automaticPresenceSimulation được đặt lại thành false, để ngăn chặn các
hoạt động passkey không mong muốn xảy ra sau này trong mã kiểm thử.

**Trường hợp 2: Mô phỏng việc nhập passkey bị hủy**

Đối với việc nhập passkey bị hủy, chúng ta phải sửa đổi một chút việc triển khai cho
trường hợp trước. Trong trường hợp nhập passkey thành công, có các sự kiện, cụ thể là
WebAuthn.credentialAdded và WebAuthn.credentialAsserted, được phát ra khi hoàn thành hoạt
động. Tuy nhiên, trình xác thực ảo WebAuthn không cung cấp bất kỳ sự kiện nào cho việc
nhập passkey bị hủy hoặc thất bại. Do đó, chúng ta phải sử dụng một cách khác để kiểm tra
việc hoàn thành một hoạt động passkey bị hủy hoặc thất bại.

Các đoạn mã sau đây mô phỏng một lần nhập passkey thất bại:

```js
async simulateFailedPasskeyInput(operationTrigger: () => Promise<void>, postOperationCheck: () => Promise<void>) {
  // set isUserVerified option to false
  // (so that subsequent passkey operations will fail)
  await client.send('WebAuthn.setUserVerified', {
    authenticatorId: authenticatorId,
    isUserVerified: false,
  });

  // set automaticPresenceSimulation option to true
  // (so that the virtual authenticator will respond to the next passkey prompt)
  await client.send('WebAuthn.setAutomaticPresenceSimulation', {
    authenticatorId: authenticatorId,
            enabled: true,
  });

  // perform a user action that triggers passkey prompt
  await operationTrigger();

  // wait for an expected UI change that indicates the passkey operation has completed
  await postOperationCheck();

  // set automaticPresenceSimulation option back to false
  await client.send('WebAuthn.setAutomaticPresenceSimulation', {
    authenticatorId,
            enabled: false,
  });
}
```

```js

test('signup with passkey', async ({ page }) => {
  // Simulate a set of user actions to trigger a passkey prompt
...

  await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in');

  // Simulate passkey input when prompted in the test
  await inputPasskey(async () => {
    await page.waitForTimeout(300);
    await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in');
  });

  // Further test steps
...
});
```

Trong hàm trợ giúp, các trình lắng nghe sự kiện được thay thế bằng một tham số promise
postOperationCheck, nó chờ một thay đổi giao diện người dùng dự kiến xảy ra trước khi
automaticPresenceSimulation có thể được đặt lại thành false.

Trong mã kiểm thử, sự khác biệt duy nhất là hàm trợ giúp phải được gọi với một promise bổ
sung để kiểm tra sự thay đổi giao diện người dùng dự kiến. Trong ví dụ trên, chúng tôi
kiểm tra rằng ứng dụng web đã điều hướng thành công đến một trang trong đó tiêu đề có văn
bản Something went wrong....

Như đã thảo luận trong Mục 6.1.1, việc hủy nhập passkey có thể không dẫn đến bất kỳ thay
đổi nào có thể quan sát được trên giao diện người dùng. Giống như ví dụ được cung cấp
trong phần đó, chúng ta phải thêm một thời gian chờ cố định trước khi kiểm tra rằng giao
diện người dùng thực sự vẫn giữ nguyên trong những trường hợp như vậy:

```js
test('signup with passkey', async ({ page }) => {
...

  await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in');

  // Simulate passkey input with a promise that triggers a passkey prompt as the argument
  await simulateFailedPasskeyInput(
          () => page.getByRole('button', { name: 'Create account with passkeys' }).click(),
          async () => {
            await page.waitForTimeout(300);
            await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in');
          }
  );

...
});
```

### 6.2. Cách kiểm thử việc tạo Passkey

Sự tiện lợi của việc sử dụng trình xác thực ảo WebAuthn được tăng cường bởi khả năng hoạt
động giống như một trình xác thực thực trong trường hợp tạo hoặc
[xóa passkey](https://www.corbado.com/vi/blog/webauthn-signal-api) bởi ứng dụng web. Một bài kiểm thử chỉ cần
thực hiện các hành động của người dùng để mô phỏng việc tạo hoặc xóa một passkey trên ứng
dụng web, và trình xác thực ảo WebAuthn tự động sửa đổi thông tin xác thực đã lưu của nó
mà không cần thêm bất kỳ công việc nào từ phía mã kiểm thử.

Đây là ví dụ về mã kiểm thử kiểm tra rằng ứng dụng web đăng ký một passkey mới vào trình
xác thực một cách chính xác:

```js
test('signup with passkey', async ({ page }) => {
...

  // Confirm there are currently no registered credentials
  const result1 = await client.send('WebAuthn.getCredentials', { authenticatorId });
  expect(result1.credentials).toHaveLength(0);

  // Perform user actions to simulate creation of a passkey credential (e.g. user registration with passkey input)
...

  // Confirm the passkey was successfully registered
  const result2 = await client.send('WebAuthn.getCredentials', { authenticatorId });
  expect(result2.credentials).toHaveLength(1);

...
});
```

Kết hợp đoạn mã này với các đoạn mã từ Mục 6.1, chúng ta có thể kiểm thử luồng đăng ký
trên trang web demo của chúng tôi. Video sau đây là một hình ảnh hóa của bài kiểm thử
trong chế độ UI của Playwright:

[Watch on YouTube](https://www.youtube.com/watch?v=HqqRgds2T94)

### 6.3. Cách kiểm thử việc xác minh Passkey

Việc xác minh một thông tin xác thực passkey với trình xác thực ảo WebAuthn hoạt động
tương tự như việc tạo một passkey, ở chỗ trình xác thực ảo tự động theo dõi số lần xác
minh được thực hiện bằng một thông tin xác thực cụ thể.

```js
test('login with passkey', async ({ page }) => {
...

  // Confirm there is only one credential, and save its signCount
  const result1 = await client.send('WebAuthn.getCredentials', { authenticatorId });
  expect(result1.credentials).toHaveLength(1);
  const signCount1 = result1.credentials[0].signCount;

  // Perform user actions to simulate verification of a passkey credential (e.g. login with passkey input)
...

  // Confirm the credential's new signCount is greater than the previous signCount
  const result2 = await client.send('WebAuthn.getCredentials', { authenticatorId });
  expect(result2.credentials).toHaveLength(1);
  expect(result2.credentials[0].signCount).toBeGreaterThan(signCount1);

...
});
```

Video sau đây trình bày một bài kiểm thử cho luồng login trên trang web demo của chúng
tôi:

[Watch on YouTube](https://www.youtube.com/watch?v=f3Vj0Dd562A)

### 6.4. Cách kiểm thử việc xóa Passkey

Mặt khác, việc xóa một passkey khỏi một ứng dụng web không nên sửa đổi bất kỳ thông tin
nào trong trình xác thực ảo WebAuthn. Ứng dụng web chỉ có thể xóa các thông tin xác thực
được lưu trong máy chủ của chính nó. Chỉ có người dùng mới có thể xóa một thông tin xác
thực passkey khỏi trình xác thực ảo WebAuthn một cách có ý thức và thủ công.

```js
test('delete a registered passkey credential', async ({ page }) => {
...

  // Confirm there is currently one registered credential
  const result1 = await client.send('WebAuthn.getCredentials', { authenticatorId });
  expect(result1.credentials).toHaveLength(1);

  // Perform user actions to simulate deletion of a passkey credential
...

  // Deleting a passkey credential from a website should not remove the credential from the authenticator
  const result2 = await client.send('WebAuthn.getCredentials', { authenticatorId });
  expect(result2.credentials).toHaveLength(1);

...
});
```

Video sau đây trình bày một bài kiểm thử cho việc xóa một thông tin xác thực passkey trên
trang web demo của chúng tôi:

[Watch on YouTube](https://www.youtube.com/watch?v=QG5_wPMY9q8)

### 6.5. Cách mô phỏng xác thực đa thiết bị

Cách trực quan nhất để mô phỏng xác thực đa thiết bị từ một thiết bị thứ hai (chưa có
passkey đã đăng ký) là chỉ cần thêm một phiên bản mới của trình xác thực ảo WebAuthn thông
qua lệnh CDP, như sau:

```js
test('signup with passkey', async ({ page }) => {
...

  // add a virtual authenticator for the first device
  const authenticatorId1 = await client.send('WebAuthn.addVirtualAuthenticator', { options });

  // perform test actions of the first device
...

  // add a virtual authenticator for the second device
  const authenticatorId2 = await client.send('WebAuthn.addVirtualAuthenticator', { options });

  // perform test actions of the second device
..
});
```

Để tránh sự phức tạp của việc quản lý ID của nhiều trình xác thực ảo, cũng có thể mô phỏng
một thiết bị mới bằng cách chỉ cần xóa các thông tin xác thực khỏi một trình xác thực duy
nhất, và thêm lại khi cần:

```js

test('signup with passkey', async ({ page }) => {
...

  const result = await client.send('WebAuthn.getCredentials', { authenticatorId });
  const credential = result.credentials[0]; // assuming only one registered passkey
  const credentialId = credential.credentialId;
  await client.send('WebAuthn.removeCredential', { authenticatorId, credentialId });

  // Perform test actions of the second device which doesn't have a registered passkey
...

  // Call if it's necessary to simulate the first device which has a registered passkey
  await client.send('WebAuthn.addCredential', { credential });

  // Perform test actions of the first device
...
});
```

Cách tiếp cận này có thể đặc biệt đơn giản hóa việc triển khai trong trường hợp cần mô
phỏng một thiết bị mới, nhưng thiết bị cũ không cần được sử dụng nữa. Trong trường hợp
này, bạn chỉ cần xóa các thông tin xác thực khỏi trình xác thực ảo và loại bỏ hoàn toàn
các thông tin xác thực của nó:

```js
test('signup with passkey', async ({ page }) => {
...

  const result = await client.send('WebAuthn.getCredentials', { authenticatorId });
  const credential = result.credentials[0]; // assuming only one registered passkey
  const credentialId = credential.credentialId;
  await client.send('WebAuthn.clearCredentials', { authenticatorId });

  // Perform test actions of the second device which doesn't have a registered passkey
...
});
```

## 7. Các giải pháp thay thế cho Trình xác thực ảo WebAuthn

Khám phá các giải pháp thay thế cho trình xác thực ảo WebAuthn có thể mang lại sự linh
hoạt trong cách kiểm thử các quy trình xác thực passkey / WebAuthn trong các dự án.

### 7.1. Kiểm thử với Dịch vụ giả lập (Mock Services)

Phát triển các dịch vụ hoặc điểm cuối giả lập có thể mô phỏng hiệu quả hành vi xác thực,
đơn giản hóa các bài kiểm thử bằng cách trừu tượng hóa sự phức tạp của cơ chế xác thực
thực tế. Cách tiếp cận này đặc biệt có lợi khi sử dụng các dịch vụ xác thực bên ngoài, cho
phép tập trung vào việc tích hợp và chức năng của các thành phần hệ thống mà không cần đi
sâu vào chi tiết xác thực.

### 7.2. Kiểm thử tích hợp với Trình xác thực thực

Để kiểm tra kỹ lưỡng các chức năng xác thực, việc sử dụng các trình xác thực thực để kiểm
thử tích hợp cung cấp một cái nhìn chi tiết về sự tương tác với các
[khóa bảo mật phần cứng](https://www.corbado.com/vi/blog/khoa-bao-mat-phan-cung-fido2-tot-nhat-2025) (ví dụ:
YubiKeys) hoặc các thiết bị sinh trắc học (ví dụ: được sử dụng trong
[Face ID](https://www.corbado.com/faq/is-face-id-passkey), Touch ID hoặc
[Windows Hello](https://www.corbado.com/glossary/windows-hello)). Mặc dù thường được tiến hành thủ công do tính
chất phức tạp của việc tích hợp các thiết bị thực tế vào các bài kiểm thử tự động, nhưng
có thể phát triển các kịch bản tự động hóa tùy chỉnh. Các kịch bản này có thể kết nối các
trình xác thực thực với các framework kiểm thử end-to-end, cung cấp một sự gần đúng hơn
với các kịch bản người dùng thực và nâng cao độ tin cậy của quy trình xác thực trong môi
trường thực tế.

## 8. Khuyến nghị cho các nhà phát triển

Sau khi trình bày các tùy chọn khác nhau và giới thiệu các đoạn mã cụ thể để kiểm thử E2E
passkey / WebAuthn với Playwright, chúng tôi còn muốn cung cấp một số khuyến nghị chung
hơn cho các nhà phát triển mới làm quen với chủ đề này.

### 8.1. Nghiên cứu bối cảnh các Framework kiểm thử E2E

Trước khi đi sâu vào việc kiểm thử passkey hoặc bất kỳ cơ chế xác thực nào khác, điều cần
thiết là phải đánh giá các framework kiểm thử E2E có sẵn và chọn tùy chọn phù hợp nhất
theo yêu cầu của dự án của bạn. Hãy xem xét sự đánh đổi giữa tốc độ và sự ổn định được
cung cấp bởi các framework dựa trên CDP như Playwright và Puppeteer, và khả năng tương
thích đa trình duyệt được cung cấp bởi các framework dựa trên WebDriver như Selenium và
Nightwatch. Trong khi các framework dựa trên CDP cung cấp tự động hóa trình duyệt nhanh
hơn và ổn định hơn, chúng bị giới hạn ở các trình duyệt dựa trên Chromium. Ngược lại, các
framework dựa trên WebDriver cung cấp khả năng tương thích đa trình duyệt rộng hơn, bao
gồm hỗ trợ cho các trình duyệt không phải Chromium như Firefox và Safari, mặc dù có thể có
hiệu suất chậm hơn và kém ổn định hơn. Hiểu rõ những sự đánh đổi này sẽ giúp bạn đưa ra
quyết định sáng suốt và chọn framework phù hợp nhất với nhu cầu của dự án.

### 8.2. Hiểu các khái niệm cơ bản đằng sau WebAuthn và Passkey

Trong khi trình xác thực ảo WebAuthn đơn giản hóa quá trình kiểm thử việc triển khai
passkey, điều quan trọng đối với các nhà phát triển là phải có một sự hiểu biết vững chắc
về các khái niệm cơ bản đằng sau tiêu chuẩn WebAuthn và passkey. Hãy làm quen với các cấu
hình khác nhau có sẵn cho trình xác thực ảo WebAuthn, chẳng hạn như protocol, transport,
hasResidentKey, hasUserVerification, và isUserVerified. Hiểu rõ các cấu hình này sẽ cho
phép bạn tinh chỉnh trình xác thực ảo để mô phỏng các kịch bản xác thực khác nhau một cách
chính xác. Ngoài ra, hãy đi sâu vào sự phức tạp của xác thực passkey, bao gồm cả việc tích
hợp của nó với các trình duyệt và thiết bị khác nhau, cũng như các cân nhắc bảo mật tiềm
ẩn. Kiến thức nền tảng này sẽ giúp bạn thiết kế các chiến lược kiểm thử toàn diện và hiệu
quả cho việc xác thực passkey trong các ứng dụng web của mình.

## 9. Tóm tắt

Hướng dẫn này đã đi sâu vào việc sử dụng trình xác thực ảo WebAuthn của CDP với
Playwright, làm nổi bật các khái niệm nâng cao và giải quyết các vấn đề không được đề cập
trong tài liệu chính thức. Chúng tôi cũng đã khám phá các giải pháp thay thế cho CDP trong
Playwright và các framework kiểm thử E2E khác. Mặc dù có các cách triển khai khác nhau,
các thông số kỹ thuật được tiêu chuẩn hóa của trình xác thực ảo WebAuthn đảm bảo sự liên
quan của hướng dẫn này trên các giao thức tự động hóa web và các framework kiểm thử
end-to-end khác nhau. Để tìm hiểu sâu hơn về các khái niệm khác nhau liên quan đến
passkey, hãy tham khảo glossary của chúng tôi về các thuật ngữ liên quan có thể giúp bạn
tinh chỉnh trình xác thực ảo WebAuthn dựa trên nhu cầu của mình.
