Get your free and exclusive +45-page Authentication Analytics Whitepaper
Quay lại tổng quan

Hướng dẫn toàn diện về lỗi WebAuthn trong môi trường Production (2026)

Tìm hiểu ý nghĩa của các lỗi WebAuthn phổ biến như NotAllowedError trong môi trường production và cách phân loại chúng theo loại hoạt động, thời gian và ngữ cảnh nền tảng.

Vincent Delitz
Vincent Delitz

Đã tạo: 9 tháng 2, 2026

Đã cập nhật: 3 tháng 7, 2026

Hướng dẫn toàn diện về lỗi WebAuthn trong môi trường Production (2026)

Trang này được dịch tự động. Đọc phiên bản gốc bằng tiếng Anh tại đây.

1. Giới thiệu#

Trong môi trường production, các lỗi WebAuthn thường gây nhầm lẫn vì các trình duyệt hiển thị một tập hợp nhỏ các tên DOMException (như NotAllowedError) có thể đại diện cho nhiều nguyên nhân cơ bản. Hơn thế nữa, phần lớn các "lỗi" - thường trên 95% trong các đợt triển khai quy mô lớn được tối ưu hóa - thực chất là hành vi dự kiến (người dùng đã hủy lời nhắc passkey của hệ điều hành).

Debugger Icon

Thử nghiệm các luồng passkey trong Passkeys Debugger.

Thử miễn phí

Quan trọng: Vì lý do bảo mật quyền riêng tư, các trình duyệt không phân biệt việc người dùng chủ động hủy hay không có passkey nào tồn tại. Tuy nhiên, trong một số tình huống và với đủ ngữ cảnh, cả trên web và nền tảng native, một số trường hợp này có thể được phân biệt bằng cách sử dụng các tín hiệu như thời gian.

Nếu bạn muốn có các định nghĩa chuẩn xác cho những tên này, hãy bắt đầu với MDN DOMException. Để biết các điều kiện cụ thể của WebAuthn dẫn đến những ngoại lệ này (và những gì trình duyệt bắt buộc phải thực thi), hãy xem Thông số kỹ thuật Web Authentication của W3C.

Nếu bạn coi mọi lỗi là "bug", bạn sẽ thực hiện sai những việc sau:

  • bạn sẽ làm bẩn số liệu lỗi của mình bằng các lượt hủy thông thường
  • bạn sẽ bỏ lỡ các lỗi hồi quy thực sự ẩn giấu bên trong vô số NotAllowedError
  • bạn sẽ tung ra giao diện người dùng (UI) gây bối rối cho người dùng thay vì giúp họ khắc phục

Trong bài viết này, chúng tôi trả lời:

  • Các tên lỗi WebAuthn phổ biến nhất thường có ý nghĩa gì trong lưu lượng truy cập thực tế?
  • Làm thế nào để bạn phân biệt NotAllowedError thành các nhóm có thể xử lý (hủy bỏ, quá thời gian, tình trạng sẵn có)?
  • Tại sao cùng một lỗi lại có ý nghĩa khác nhau tùy thuộc vào hoạt động (đăng nhập conditional UI, đăng nhập modal, tạo passkey, conditional create)?
  • Ngữ cảnh tối thiểu nào bạn nên nắm bắt để "nó thất bại" trở thành một vấn đề có thể tái hiện?
Thông tin chính
  • NotAllowedErrortín hiệu bề mặt, không phải nguyên nhân cốt lõi. Nó có thể có nghĩa là hủy bỏ, quá thời gian, "không có thông tin xác thực cục bộ" hoặc thiếu sự kích hoạt của người dùng tùy thuộc vào ngữ cảnh.
  • Loại hoạt động làm thay đổi ý nghĩa. Cùng một NotAllowedError có ý nghĩa khác nhau trong quá trình đăng nhập conditional UI, đăng nhập modal, tạo passkey thủ công, conditional create và thao tác nối thêm được kích hoạt tự động.
  • Thời gian từ lúc bắt đầu hoạt động là tín hiệu bị đánh giá thấp nhất: ngay lập tức (<1s), người dùng hủy (1-15s) và quá thời gian (30s+) là các danh mục hoàn toàn khác nhau.
  • AbortError thường là sự cố vòng đời/đồng thời (điều hướng, render lại, nhiều yêu cầu đang xử lý).
  • SecurityError gần như luôn là cấu hình/ngữ cảnh và hiếm gặp trong các đợt triển khai production hoàn thiện.
  • "Tên lỗi trình duyệt" và "lỗi xác minh máy chủ từ chối" là các lớp khác nhau. Hãy theo dõi các lỗi từ chối của máy chủ dưới dạng mã rõ ràng, không phải là lỗi máy khách chung chung.
  • Tên lỗi thô không thể tự hành động. Luôn nắm bắt loại hoạt động, thời gian và ngữ cảnh nền tảng cùng với error.name để bạn có thể phân loại các lỗi thành các nhóm mà bạn thực sự có thể sửa chữa.
  • "Môi trường" không chỉ là Trình duyệt + Hệ điều hành. Để thực sự hiểu các lỗi, bạn cần theo dõi sự kết hợp đầy đủ: Phiên bản hệ điều hành, Máy khách (Trình duyệt/Phiên bản ứng dụng), Cài đặt Authenticator (ví dụ: trạng thái iCloud/GPM) và Mẫu phần cứng cụ thể.
  • Lỗi đăng nhập là P1, Lỗi tạo là P2. Trong khi các lỗi tạo (P2) thường có số lượng lớn hơn do người dùng từ bỏ, các lỗi đăng nhập (P1) chặn quyền truy cập và yêu cầu cảnh báo ngay lập tức.

2. Bảng tham khảo Production#

Nếu bạn chỉ cần một sơ đồ phân loại nhanh để gỡ rối quá trình sửa lỗi, hãy bắt đầu với bảng này. Nó tập trung vào những gì các nhóm thực sự thấy trong dashboard và các ticket hỗ trợ.

error.nameÝ nghĩa thường gặp trong productionCần kiểm tra gì để xác nhậnHành động đầu tiên (UX + kỹ thuật)
NotAllowedErrorNgười dùng đã đóng bảng điều khiển, hết thời gian chờ hoặc không khớp tính khả dụng đều gộp chung vào một nhóm. Đây là nhóm lỗi lớn nhất.thời gian đến khi lỗi, liệu mã QR/giao diện người dùng kết hợp có xuất hiện không, liệu nghi thức có bắt đầu từ một hành động của người dùng khôngCoi như bình thường: khôi phục UI + hiển thị phương án dự phòng
AbortErrorỨng dụng của bạn (hoặc trình duyệt) đã hủy bỏ nghi thứcđiều hướng/hiển thị lại trong quá trình; các lệnh gọi WebAuthn đồng thời; AbortController.abort()Bắt buộc có một yêu cầu đang xử lý; ngăn chặn thay đổi định tuyến; xử lý lỗi hủy như luồng điều khiển bình thường
SecurityErrorNgữ cảnh/chính sách không được phépchiến lược origin + RP ID; iframe/nhúng; HTTPS; tính năng policyKhắc phục cấu hình RP ID/origin; xác nhận các chính sách nhúng; đảm bảo ngữ cảnh an toàn
InvalidStateErrorTrạng thái không khớp (thường là đăng ký trùng lặp)đăng ký với đăng nhập; excludeCredentials; thông tin xác thực hiện tại trên trình xác thựcCoi như “đã đăng ký”; điều chỉnh đường dẫn UX; sửa lại phần tạo tùy chọn
ConstraintErrorKhông thể đáp ứng các yêu cầuauthenticatorAttachment, userVerification, yêu cầu khóa thường trúNới lỏng ràng buộc hoặc cung cấp đường dẫn/phương án dự phòng thay thế. Ví dụ: Khóa màn hình bị thiếu trên Android
DataErrorDữ liệu đầu vào bị sai lệch/không nhất quánmã hóa base64url; định dạng id/challenge/user handleKhắc phục việc mã hóa/sắp xếp chuỗi; thêm xác thực trong phần tạo tùy chọn
NotSupportedErrorNền tảng/trình duyệt không hỗ trợ những gì bạn yêu cầuHệ điều hành/phiên bản trình duyệt; giả định phát hiện tính năngTrở lại trạng thái mặc định ngay lập tức; ghi chú phân đoạn; tránh hiển thị CTA passkey cho các môi trường không được hỗ trợ
UnknownErrorNền tảng/trình xác thực thất bại theo một cách chung chungtăng đột biến sau khi cập nhật hệ điều hành; số hiệu bản dựng thiết bị; lỗi nhà cung cấp trình quản lý thông tin xác thựcGiao diện người dùng thân thiện với tính năng thử lại; ghi lại các số hiệu bản dựng; điều tra các mức tăng đột biến theo phân đoạn

Một điều dễ bỏ sót: cùng một error.name có thể mang ý nghĩa rất khác nhau tùy thuộc vào loại hoạt động. Hãy luôn lưu ý ngữ cảnh hoạt động khi bạn đọc các phần bên dưới. Trong thực tế, các lỗi tạo passkey (đăng ký) thường nhiều hơn lỗi đăng nhập rất nhiều - bảng trên áp dụng cho cả hai, nhưng phần tạo mới là nơi tập trung khối lượng lớn.

Tiếp theo, chúng ta sẽ đi sâu hơn vào NotAllowedError vì đây là lỗi bạn sẽ thấy nhiều nhất và là lỗi mà các nhóm thường hiểu sai nhất.

3. NotAllowedError: Hoạt động đã bị quá thời gian hoặc không được phép#

NotAllowedError thường giống như "passkeys thất bại", nhưng nó thường là nền tảng đang báo cho bạn biết người dùng đã không hoàn thành UI của hệ điều hành. Chìa khóa ở đây là chia nó thành các nhóm mà bạn có thể xử lý.

Những gì bạn sẽ thấy trong bảng điều khiển trình duyệt:

NguồnThông báo lỗi
Chrome, EdgeNotAllowedError: The operation either timed out or was not allowed. See: https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.
Safari, WebKitNotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
Safari, WebKitNotAllowedError: This request has been cancelled by the user.
Chrome, EdgeNotAllowedError: The operation is not allowed at this time because the page does not have focus.
Safari, WebKitNotAllowedError: The document is not focused.
FirefoxNotAllowedError: Operation failed.

Tất cả những lỗi này hiển thị dưới dạng error.name === "NotAllowedError". error.message khác nhau tùy theo công cụ trình duyệt và nguyên nhân cơ bản, nhưng kết quả đều giống nhau: nghi thức không hoàn tất.

Điều này áp dụng cho cả đăng nhập và tạo passkey. Trong quá trình đăng nhập (conditional UI, modal có hoặc không có allowList), NotAllowedError thường có nghĩa là người dùng đã không hoàn thành nghi thức. Trong quá trình tạo passkey, cùng một lỗi hiển thị vì nhiều lý do khác nhau: người dùng đã đóng hộp thoại tạo, conditional create không hoạt động, hoặc trang bị mất tiêu điểm trong quá trình thao tác nối thêm được kích hoạt tự động. Loại hoạt động thay đổi ý nghĩa của lỗi và những gì bạn nên làm với nó.

Thời gian thường là một tín hiệu bị đánh giá thấp. Một lỗi xảy ra dưới 1 giây sau khi nhấp chuột thường là từ chối ngay lập tức (môi trường không thể làm được, tài liệu không được chú ý, thiếu khả năng). Một lỗi xảy ra sau vài giây là do người dùng hủy bỏ (họ thấy hộp thoại và quyết định không tiếp tục). Một lỗi sau hơn 30 giây là đã quá thời gian. Trên các nền tảng native, thời gian đặc biệt quan trọng: thời gian khứ hồi của authenticator, lời nhắc sinh trắc học và quá trình chuyển giao ứng dụng quản lý mật khẩu đều có khoảng thời gian đặc trưng giúp bạn phân biệt "không hiệu quả" với "người dùng đã rời đi". Bạn vẫn không thể dễ dàng phân biệt được liệu một passkey có tồn tại hay không.

3.1 Phân biệt theo ngữ cảnh#

Bạn không cần một tín hiệu hoàn hảo. Bạn cần đủ ngữ cảnh để tránh việc đối xử với mọi lỗi NotAllowedError theo cùng một cách. iOS/Safari nhận được sự chú ý đặc biệt ở bên dưới vì nó có những hạn chế riêng (yêu cầu kích hoạt người dùng ở các phiên bản trước), nhưng về số lượng lỗi thô, các trình duyệt Windows và Chromium thường tạo ra nhiều lỗi NotAllowedError hơn bất kỳ nền tảng nào khác. Những tín hiệu này thường giúp bạn đi được 80% chặng đường:

Tín hiệuÝ nghĩa có thểLàm gì tiếp theo
Lỗi ngay lập tức (<1s)Môi trường từ chối: không có khả năng, trang không được chú ý, bề mặt conditional create không có sẵnKiểm tra xem có hỗ trợ tính năng không; đảm bảo trang đang được chú ý; xác nhận hoạt động này được hỗ trợ trên nền tảng này
Hủy nhanh (1-3s)Nhắc nhở bất ngờ / không có ngữ cảnhThay đổi thời gian nhắc nhở; thêm thời gian chờ sau khi hủy
Thời gian hủy của người dùng (3-15s)Người dùng đã nhìn thấy hộp thoại và chọn không tiếp tụcUX dự kiến; khôi phục UI + hiển thị phương án dự phòng
Hết thời gian chờ (30s+)Nghi thức đã quá thời gian mà không có thao tác từ người dùngNhóm lại là "không hoàn tất"; xem xét liệu lời nhắc nhở có được chú ý hay không
Giao diện mã QR/kết hợp xuất hiện trước khi bị lỗiKhông có thông tin xác thực cục bộ trên thiết bị này. Lưu ý: Việc phát hiện sớm các quyết định mã QR yêu cầu một lớp passkey intelligence có thể nhận diện liệu chứng nhận nào có thể sử dụng đang tồn tại trên thiết bị hiện tại không.Tạo cổng các đề nghị mật mã khóa; hiển thị tùy chọn "Sử dụng điện thoại" rõ ràng; giảm mã QR bất ngờ
Tập trung trên iOS/Safari và bị kích hoạt mà không có click/chạmThiếu kích hoạt từ người dùngBắt đầu nghi thức từ một thao tác thực tế của người dùng
Trong quá trình conditional create hoặc tự động đính kèmTự động điền không có sẵn, thông tin đăng nhập đã tồn tại, hoặc trang không được chú ý. Các lỗi conditional create có thể xuất hiện đột ngột và với số lượng lớn khi tính năng được tung ra, biến nó thành một trong những nguồn lỗi lớn nhất chỉ trong một đêm.Xem conditional create; kiểm tra trạng thái khả năng hiển thị của trang; sử dụng getClientCapabilities() để xác nhận sự hỗ trợ của conditionalCreate trước khi thử gọi

Đây cũng là lý do tại sao NotAllowedError hiếm khi nên hiển thị cho người dùng. Nó không phải là thông báo mà người dùng có thể thực hiện.

Việc phân loại theo bối cảnh cũng là lúc tỷ lệ thành công bị chia cắt mạnh nhất. Phân tích tỷ lệ thành công của passkey đo lường tỷ lệ hoàn thành ở Q1 2026 đạt 55-95% cho các luồng xác định thiết bị chưa biết so với 95-99% đối với thiết bị đã biết trả về trên các triển khai B2C quy mô lớn. Nhận dạng nền tảng web iOS trước tiên đạt 85-95% hoàn thành (CDA 0-5%), nền web Android ở 70-85% (CDA 5-10%) và nền web macOS ở 70-85% (CDA 10-15%), trong khi nền web Windows nằm ở mức 45-60% với %CDA ở 55-65% trên Windows 11 và 40-55% trên Windows 10. Đọc khối lượng NotAllowedError mà không chia thiết bị đã biết với chưa biết kết hợp hai cơ chế tỷ lệ thành công hoàn toàn khác nhau.

Một sắc thái quan trọng trong production: một số tác nhân người dùng có thể hiển thị hết thời gian dưới dạng TimeoutError, nhưng nhiều nhóm vẫn thấy tình trạng hết thời gian chờ được thu gọn thành NotAllowedError trong trang tổng quan. Dù bằng cách nào, hãy coi hết thời gian chờ là “nghi thức không hoàn thành” và phân nhóm sử dụng thời gian cùng ngữ cảnh.

3.2 Xử lý UX: Biến việc Hủy (Cancel) thành luồng thoát bình thường#

Khi bảng điều khiển hệ điều hành bị tắt hoặc hết thời gian chờ, UI của bạn phải ngay lập tức được khôi phục lại và phản ứng mượt mà. Một số quy tắc thực tế:

  • khôi phục UI đăng nhập (không hiển thị các spinner đang chạy)
  • giữ nguyên trạng thái định danh (không buộc người dùng nhập lại)
  • hiển thị một luồng fallback rõ ràng trên cùng một màn hình
  • tránh các biểu ngữ mang tính hù dọa đối với các kết quả đã được dự kiến trước

Ngoài những điều cơ bản:

  • Xử lý lần hủy đầu tiên như một điều bình thường và cung cấp tùy chọn thử lại với một lời giải thích bình tĩnh. Chỉ sau lần hủy thứ hai, bạn mới nên gợi ý các tùy chọn dự phòng.
  • Cho phép xuất hiện tối đa ba lời nhắc tạo thông tin đăng nhập trước thời gian chờ để những người dùng bất ngờ có cơ hội thứ hai.
  • Chia lỗi thành hai phần tính đếm số lượng lỗi tạo thông tin đăng nhập và đăng nhập để bạn có thể so sánh hợp lý.
  • Phân khúc tỷ lệ lỗi dựa theo Hệ điều hành, trình duyệt và thiết bị để bạn có thể nhận diện xem rào cản thực sự nằm ở đâu.

Nếu tỷ lệ “lượt hủy” thực sự cao, bước tiếp theo là khắc phục nguyên nhân gốc rễ đằng sau: thời gian xuất hiện của lời nhắc, những bất ngờ từ mã QR và mức độ sẵn có thấp.

3.3 Khắc phục lỗi Kỹ thuật để giảm NotAllowedError#

Hãy bắt đầu với những thay đổi có thể nhanh chóng cải thiện các số liệu:

  • Chỉnh sửa thời gian nhắc nhở và kích hoạt người dùng (đặc biệt là trên iOS/Safari).
  • Giảm thiểu yếu tố bất ngờ đối với mã QR/kết hợp.
  • Cấp quyền cung cấp passkey để các tính năng này chỉ được hiển thị khi khả năng thành công cao.
  • Sử dụng các hình thức để tránh hiện lời nhắc nhở khi không có thông tin xác thực cục bộ.
  • Xử lý tình trạng mất kết nối mạng: Lỗi mạng trong quá trình xác minh thường được hiển thị như những lỗi chung chung trên máy khách. Triển khai một hàng đợi offline cho các bản ghi từ xa để bạn không bị mất dữ liệu lỗi khi kết nối của người dùng bị ngắt trong quá trình thực hiện phương thức.
  • Tạo giới hạn phương thức thông qua API phát hiện để lỗi môi trường không thổi phồng nhóm NotAllowedError của bạn. Hãy bắt đầu với isUVPAA() như rào cản cơ bản nhất, sau đó sử dụng getClientCapabilities() cho những yêu cầu kiểm tra kỹ càng hơn (conditional create, conditional get, vận chuyển lai, trình xác thực nền tảng). Lưu ý rằng các API phát hiện có thể bị gián đoạn cùng với những lần cập nhật hệ điều hành: iOS 26.2 phát hành với một lỗi trên WebKit trong đó isUVPAA() trả về giá trị false đối với tất cả những trình duyệt có nền tảng WKWebView mặc dù passkey hoạt động hoàn toàn bình thường, dẫn tới đột biến đột ngột ở lỗi NotAllowedError đối với 10-25% số lượng người dùng iOS.

3.4 Ghi chú về việc các tên Lỗi ngày càng tăng#

Tên lỗi là một mục tiêu luôn di chuyển. Đã có những đề xuất đang được thực hiện để thêm nhiều các lỗi chi tiết hơn liên quan đến WebAuthn (ví dụ, để chia ra “không có thông tin xác nhận” với “người dùng đã hủy”). Tính đến tháng 2 năm 2026, điều này vẫn chưa được áp dụng vào bất kỳ trình duyệt nào, nên tốt nhất là xây dựng hệ thống các nhóm lý do để phân loại nguyên nhân cho riêng mình dựa trên ngữ cảnh và thời gian. Nếu bạn muốn theo dõi hoạt động này, xin hãy xem WebAuthn issue #2062 và bảng giải thích "Mã Lỗi Mới".

Những lỗi khác sẽ xuất hiện ít thường xuyên hơn nhưng vẫn đáng để tìm hiểu khi chúng xuất hiện.

4. AbortError: Hoạt động đã bị gián đoạn#

Lỗi AbortError ít khi xuất hiện với số lượng nhiều nếu so với lỗi NotAllowedError, nhưng khi nó xảy ra, lỗi này thường là chẩn đoán mức cao: nghĩa là nghi thức không hoàn thiện bởi vì ứng dụng của bạn không cho yêu cầu được duyệt - điều hướng xảy ra, trạng thái thay đổi hoặc một yêu cầu thứ hai đã được khởi động.

Bạn sẽ gặp những lỗi nào trên giao diện trình duyệt:

NguồnThông báo lỗi
Chrome, EdgeAbortError: The operation was aborted.
Chrome, EdgeAbortError: Aborted by AbortSignal.
FirefoxAbortError: signal is aborted without reason
FirefoxAbortError: Operation timed out.
Safari, WebKitAbortError: The user aborted a request.
ChromeAbortError: CredentialContainer request is not allowed.

Những nguyên nhân thường gặp trong môi trường production bao gồm:

  • Cuộc gọi tới WebAuthn được thực hiện đồng thời ở nhiều nơi khác nhau (hai lượt nhắc nhở bị trùng lặp)
  • Thay đổi định tuyến/tái kết xuất khi nghi thức đang chạy
  • Gọi AbortController.abort() trong lúc chờ thử lại hoặc lúc chờ dọn dẹp hệ thống

Để giải quyết, hãy tập trung vào việc tạo cho những nghi thức một “khu vực kiểm soát”:

  • Chỉ cho phép một yêu cầu được hoạt động trong cùng một thời điểm
  • Chặn điều hướng trong suốt quá trình nghi thức diễn ra (hoặc hủy bỏ sạch sẽ và hồi phục lại lại UI)
  • Coi việc bị gián đoạn là điều khiển luồng mong đợi: hiện lên phím thử lại và luồng fallback

Nếu bạn bắt gặp lỗi AbortError xuất hiện đặc ở những bề mặt được đính kèm hoặc trong những ứng dụng hỗ trợ nhiều tên miền, phần tiếp theo bạn nên kiểm tra chính là SecurityError.

5. SecurityError: WebAuthn không hỗ trợ trong những web bị lỗi trên chứng chỉ TLS#

SecurityError là cách trình duyệt thông báo rằng: "bối cảnh này không được phép làm những gì bạn vừa bảo." Về thực tế thì hầu hết tất cả lỗi đều nằm trên cấu hình chứ không phải trên thao tác của người dùng. Trong các đợt phát hành ở production, SecurityError là rất hiếm bởi vì những lỗi trên thường được tìm thấy khi kiểm tra tích hợp. Khi nó hiện lên trên giao diện hoạt động thì có nghĩa là một domain mới, bối cảnh đính kèm hay một nơi điều hướng vừa mới được thêm vào mà thiếu đi các cấu hình WebAuthn hỗ trợ.

Những nguyên do thông dụng bao gồm:

  • Không khớp RP ID / nguồn gốc (cho setup gồm nhiều domain)
  • Hạn chế đính kèm tên miền chéo (iframe)
  • Không đảm bảo được độ an toàn (thiếu HTTPS) hoặc bị chặn phân quyền/chính sách
  • .well-known/webauthn hay .well-known/assetlinks.json bị cấu hình sai, bị mất hoặc tạm thời không thể sử dụng. Những lỗi trên hệ thống mạng trong quá trình cần thiết nhất khi mà trình duyệt thu thập thông tin từ những tập tin này sẽ gây ra thất bại. Một góc khuất mà rất nhiều người dùng không để ý: nếu web chính của bạn phải bảo trì, thì những file public này cũng sẽ offline, làm gián đoạn mọi nghi thức liên quan tới WebAuthn của bạn đối với tất cả những bên lệ thuộc vào nó.

Bạn sẽ gặp những lỗi nào trên giao diện trình duyệt:

NguồnThông báo lỗi
Chrome, EdgeSecurityError: WebAuthn is not supported on sites with TLS certificate errors.
Any browserSecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current origin's effective domain.
Chrome (iframe)SecurityError: The 'publickey-credentials-create' feature is not enabled in this document.

Trong môi trường production, SecurityError là rất hiếm - hầu hết chúng thường được tìm thấy và loại bỏ trong thời gian kiểm tra. Nếu chúng vẫn xuất hiện, thì lỗi trên chứng chỉ TLS là những lý do duy nhất có khả năng xảy ra nhất.

Vòng lập debug nhanh nhất là:

  • Ghi lại các đầu vào origin và RP ID bạn đã dùng
  • Tái tạo lại những trong cùng hoàn cảnh như lúc xảy ra lỗi (Top-level vs iframe, Prod Domain vs Staging)
  • Nếu bạn đính kèm thông tin để đăng nhập, hãy chắc chắn về chính sách phân quyền đã được cấu hình từ trước (ví dụ như publickey-credentials-create / publickey-credentials-get): MDN Permissions-Policy
  • Xác nhận chiến lược đặt tên miền của bạn
  • Nếu bạn sử dụng iframe, hãy xác nhận chính sách của hệ thống cho các tiện ích của người dùng

Một khi lỗi SecurityError được giải quyết, những loại lỗi tiếp theo mà bạn cần phải giải quyết kỹ lưỡng là những lỗi thường thể hiện rõ về bug đã được cài đặt: InvalidStateError, ConstraintErrorDataError.

PasskeysCheatsheet Icon

Cheatsheet Passkeys. Hướng dẫn thực tế, mẫu triển khai và KPI cho chương trình passkeys.

Nhận cheat sheet

6. InvalidStateError, ConstraintError, DataError: coi như Implementation Bugs#

Các lỗi này hiếm khi xảy ra trong một hệ thống passkey hoàn thiện. Khi chúng xuất hiện, chúng thường chỉ ra rằng phần tạo tùy chọn (option generation) bị sai cho một nhóm nhất định hoặc quy trình đang ở sai trạng thái.

6.1 InvalidStateError: "credentials already registered with the relying party"#

Những gì bạn sẽ thấy trong bảng điều khiển trình duyệt:

NguồnThông báo lỗi
Safari, WebKitInvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.
Chrome, EdgeInvalidStateError: At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.
Chrome, EdgeInvalidStateError: A request is already pending.
FirefoxInvalidStateError: An attempt was made to use an object that is not, or is no longer, usable

Ý nghĩa thông thường:

  • đăng ký: bạn đã cố tạo một credential đã tồn tại (trùng lặp)
  • đăng nhập: ít phổ biến hơn; thường có nghĩa là luồng/trạng thái không nhất quán đối với nền tảng

Xử lý thực tế:

  • nếu xảy ra trong đăng ký thủ công, hãy coi nó như “đã được đăng ký” và định tuyến cho phù hợp
  • đảm bảo excludeCredentials liệt kê tất cả credential ID hiện có của người dùng để authenticator có thể phát hiện lỗi trùng lặp
  • trong lúc conditional create, InvalidStateError là điều được dự kiến và nên được bỏ qua một cách lặng lẽ: điều đó có nghĩa là passkey đã tồn tại trong nhà cung cấp. Điều tương tự áp dụng cho NotAllowedErrorAbortError trong quá trình conditional create.

6.2 ConstraintError#

Ý nghĩa thông thường: authenticator không thể đáp ứng các ràng buộc mà bạn yêu cầu.

Nguyên nhân phổ biến:

  • thiếu khóa màn hình thiết bị (đặc biệt là Android): nền tảng yêu cầu xác minh sinh trắc học hoặc mã PIN nhưng thiết bị chưa được cấu hình khóa màn hình
  • quá khắt khe đối với các giả định về authenticatorAttachment hoặc khóa thường trú
  • yêu cầu bắt buộc userVerification trong các nhóm mà chúng không có sẵn

Cách khắc phục: nới lỏng các ràng buộc (khi có thể chấp nhận) hoặc cung cấp một hướng đi thay thế. Đối với thiết bị thiếu khóa màn hình, hãy xem xét phát hiện tình trạng này và hướng dẫn người dùng thay vì thất bại lặng lẽ (Android).

6.3 DataError#

Ý nghĩa thông thường: đầu vào bị biến dạng hoặc không nhất quán.

Nguyên nhân phổ biến:

  • sai lầm khi mã hóa (base64 so với base64url)
  • id xác thực / định dạng challenge không hợp lệ

Cách khắc phục: xác minh và chuẩn hóa các đầu vào ở ranh giới nơi bạn tạo WebAuthn options. Trong thực tế, DataError gần như vắng bóng trong các hệ thống production trưởng thành - nếu option generation của bạn đã được thử nghiệm, bạn sẽ không thấy điều này trong dashboard.

Nếu các lỗi này đang được kiểm soát, câu hỏi tiếp theo là phạm vi hỗ trợ: người dùng có thất bại vì môi trường không thể thực hiện WebAuthn theo cách bạn mong đợi?

7. NotSupportedError: User agent không hỗ trợ public key credentials#

NotSupportedError là một tín hiệu về độ bao phủ, không phải tín hiệu về độ tin cậy. Nó thường có nghĩa là một phân khúc không thể làm những gì bạn yêu cầu (HĐH/trình duyệt quá cũ, thiếu khả năng, tính năng không được kích hoạt).

Những gì bạn sẽ thấy trong bảng điều khiển trình duyệt:

NguồnThông báo lỗi
Chrome, EdgeNotSupportedError: The user agent does not support public key credentials.
FirefoxNotSupportedError: Resident credentials or empty 'allowCredentials' lists are not supported.
Chrome, Edge, FirefoxTypeError: PublicKeyCredential.parseCreationOptionsFromJSON is not a function
Chrome, Edge, FirefoxTypeError: PublicKeyCredential.parseRequestOptionsFromJSON is not a function
Chrome, Edge, FirefoxTypeError: credential.toJSON is not a function
SafariTypeError: Can only call PublicKeyCredential.toJSON on instances of PublicKeyCredential

Hai dòng đầu tiên là các DOMException thực sự cho NotSupportedError. Các mục TypeError về mặt kỹ thuật là một loại ngoại lệ khác nhưng đại diện cho cùng một lớp vấn đề: trình duyệt hoặc môi trường không hỗ trợ những gì bạn đã yêu cầu. Lớp chuỗi hóa JSON TypeError phổ biến hơn nhiều trong thực tế so với bản thân DOMException NotSupportedError.

Nguyên nhân phổ biến bao gồm:

  • phiên bản trình duyệt/HĐH cũ hơn không hỗ trợ WebAuthn cơ bản
  • yêu cầu các tính năng WebAuthn không khả dụng trên nền tảng đó
  • thử nghiệm các luồng riêng biệt theo nền tảng trên các thiết bị không được hỗ trợ

Lớp chuỗi hóa JSON là nguồn lớn nhất gây ra lỗi thuộc lớp NotSupportedError trong môi trường production. Về kỹ thuật, chúng hiển thị là TypeError (thiếu phương thức) chứ không phải là DOMException, nhưng đây là nơi bạn sẽ gặp chúng. Hai nguyên nhân gốc rễ khác biệt:

  1. Trình duyệt không hỗ trợ các phương thức chuỗi hóa JSON của WebAuthn. Trình duyệt có navigator.credentials nhưng không có PublicKeyCredential.parseCreationOptionsFromJSON / parseRequestOptionsFromJSON. Điều này chiếm khoảng 90% gia đình lỗi này, tập trung ở các phiên bản Safari và Chrome cũ hơn. Nếu thư viện client của bạn phụ thuộc vào các phương thức này mà không có dự phòng, thì điều này sẽ tạo ra lượng lỗi đáng kể.
  2. Các tiện ích mở rộng trình quản lý mật khẩu phá vỡ .toJSON(). Các tiện ích mở rộng như Bitwarden, LastPass, hoặc 1Password có thể chặn buổi lễ và trả về một đối tượng trông giống như thông tin xác thực nhưng không phải là phiên bản thực sự của PublicKeyCredential. Việc gọi .toJSON() trên đó có thể ném ra lỗi, trả về undefined, hoặc đối tượng hoàn toàn là null. Điều này chiếm khoảng 10% nhưng đặc biệt gây bối rối để gỡ lỗi vì các thông báo lỗi khác nhau tùy theo trình duyệt.

Xử lý nên dứt khoát và nhanh chóng:

  • quay về dự phòng mật khẩu/OTP ngay lập tức
  • ghi lại phân khúc để bạn có thể định lượng khoảng trống bao phủ
  • tránh hiển thị CTA của passkey trên các phân khúc sẽ liên tục gặp lỗi

Nếu phạm vi hỗ trợ có vẻ ổn định nhưng vẫn gặp lỗi ở các nhóm thiết bị cụ thể, bạn có thể đang xử lý các sự cố ở tầng nền tảng được hiển thị thông qua UnknownError.

8. UnknownError: Một lỗi không xác định xảy ra khi giao tiếp với trình quản lý thông tin xác thực#

UnknownError là một khái niệm tổng hợp để chỉ các lỗi về trình xác thực/hệ điều hành không phù hợp để xếp vào các loại lỗi khác. Thường đây chỉ là vấn đề tạm thời nhưng nó cũng có thể tăng vọt sau khi cập nhật hệ điều hành.

Những lỗi bạn sẽ thấy trên bảng điều khiển trình duyệt:

NguồnThông báo lỗi
Chrome (Android)UnknownError: An unknown error occurred while talking to the credential manager.
Mọi trình duyệtUnknownError: The operation failed for an unknown transient reason.
Mọi trình duyệtUnknownError: Either the device has received unexpected request data, or the device has been reconfigured since the request was made.
Mọi trình duyệtUnknownError: Something went wrong.
Chrome (LastPass)TypeError: Cannot use 'in' operator to search for 'type' in null
Safari (LastPass)TypeError: null is not an Object. (evaluating 'key in input')
Chrome (Bitwarden)FallbackRequested

Cách xử lý trong thực tế:

  • thiết kế UX hỗ trợ thử lại thân thiện với người dùng (không đổ lỗi cho người dùng)
  • thu thập phiên bản hệ điều hành/bản dựng cũng như ngữ cảnh về trình quản lý thông tin xác thực nếu có thể
  • theo dõi hiện tượng gia tăng đột biến lỗi ở một phân khúc sau khi cập nhật hệ điều hành

Một nguồn lỗi nhỏ không phù hợp với loại DOMException: các tiện ích mở rộng về trình quản lý mật khẩu của trình duyệt (như Bitwarden, LastPass, 1Password, và những ứng dụng khác) có thể chặn cuộc gọi API WebAuthn và đưa ra các phản hồi phi tiêu chuẩn. Dù không nhiều nhưng đây là những lỗi cần theo dõi vì chúng ảnh hưởng nhất quán đến các phân khúc người dùng cụ thể và những triệu chứng đi kèm thì rất khó hiểu: phương pháp không có trong đối tượng xác thực được trả về, loại lỗi bất ngờ, hoặc các phản hồi bất thường không phù hợp với thông tin lỗi WebAuthn đã cung cấp. Chúng thường sẽ hiển thị dưới dạng UnknownError hoặc dạng ngoại lệ chưa phân loại. Nếu bạn thấy tỉ lệ gặp lỗi tăng cao ở các trình duyệt cụ thể mà không có lý giải cho cấp độ hệ điều hành, hãy kiểm tra xem có sự can thiệp của tiện ích mở rộng nào không.

Từ nãy đến giờ chúng ta đã nhắc qua tất cả các tên lỗi trên trình duyệt web. Nhưng nếu bạn sử dụng các ứng dụng native thì bức tranh tổng thể về lỗi sẽ khác biệt - và có phần tốt hơn nhiều.

9. Đôi lời về Native Apps (iOS và Android)#

Tất cả những thông tin bên trên là dành cho các trình duyệt web. Ứng dụng native - iOS với khung cấu trúc ASAuthorization, Android với Credential Manager - đều chia sẻ các nhóm lỗi cơ bản chung nhưng khác biệt ở các điểm quan trọng sau:

  1. "Không có credentials" là một tín hiệu khác biệt. Trên web, các trình duyệt sẽ gộp hai trường hợp "không có credential khả dụng" và "người dùng hủy" vào chung một loại lỗi NotAllowedError vì các lý do liên quan đến bảo mật quyền riêng tư. Trên nền tảng native, việc dùng lệnh preferImmediatelyAvailableCredentials trên iOS (ASAuthorizationController) hoặc setPreferImmediatelyAvailableCredentials(true) trên Android (GetCredentialRequest) sẽ báo cho hệ điều hành chỉ hiện các loại credential đã có sẵn trong thiết bị và báo lỗi ngay nếu không tìm thấy. Từ đó ta sẽ thu được một lệnh "không có credentials" rõ ràng mà trên nền tảng web không thể làm được.

  2. Trạng thái về nhà cung cấp credential được hiển thị. Nền tảng native trong một số điều kiện có thể thông báo cho bạn khi không có nhà cung cấp credential nào (Google Password Manager, iCloud Keychain, 1Password, v.v.) được cài đặt, được định cấu hình, hoặc được cài mặc định và phản hồi lại. Trên web, loại thông tin này bị ẩn sau các thông báo lỗi NotAllowedError khó hiểu.

  3. Thông báo lỗi mang tính chi tiết hơn. Khi người dùng đã tải app về, hệ điều hành sẽ hiển thị chi tiết các lỗi chẩn đoán sâu hơn. Các lý do liên quan đến bảo mật quyền riêng tư làm cho trình duyệt web mù mờ sẽ không áp dụng ở đây khi mà ứng dụng đã được tải về. iOS sẽ hiển thị thông báo với ngôn ngữ được thiết bị sử dụng. Android hiển thị các loại lỗi có cấu trúc cùng với chuỗi phân tích. Từ đó giúp cho việc sửa lỗi dễ hơn nhưng đồng nghĩa với việc bạn phải biết nguyên do xảy ra lỗi với dạng lỗi của hệ điều hành hiện tại.

9.1 iOS (Khung cấu trúc ASAuthorization)#

iOS xuất lỗi passkey bằng khung cấu trúc ASAuthorization. Tất cả lỗi sẽ chuyển về authorizationController(controller:didCompleteWithError:) dưới dạng đối tượng NSError.

Phân nhóm dựa vào domain và code, không phải vào tin nhắn. Main error domain là com.apple.AuthenticationServices.AuthorizationError (ASAuthorizationError.errorDomain). Cast lỗi với let nsError = error as NSError và gán với .domain.code. Đừng bao giờ chọn .localizedDescription trong lúc đang phát hành bản production - tin nhắn của Apple được dịch qua hơn 30 ngôn ngữ và có thể thay đổi trên các phiên bản hệ điều hành. Các dòng tin nhắn phía dưới rất hữu dụng trong việc nhận biết các lỗi trong bản báo cáo nhưng lại không phải là tiêu chí để ta phân loại lỗi.

Mã lỗi Public ASAuthorizationError:

CodeTênSinceÝ nghĩa
1000UnknowniOS 13Lỗi không xuất hiện trong bản product.
1001CancelediOS 13Lỗi tương đương NotAllowedError. Người dùng tắt danh sách passkey. Đây là lỗi phổ biến nhất - loại tín hiệu sạch (clean signal) có chỉ số rỗng trên userInfo và không ẩn chứa các lỗi tiềm tàng.
1002InvalidResponseiOS 13Hỏng hóc liên quan đến cấp độ khung cấu trúc. Hiếm gặp trong lúc thực hành.
1003NotHandlediOS 13Request không được provider nào giải quyết. Kiểm tra lại thông tin về provider và quyền lợi (entitlements).
1004FailediOS 13Thất bại chung chung. localizedDescription mang lý do chính xác (ví dụ "Application with identifier X is not associated with domain Y"). userInfo có thể có chuỗi FailureReason, nhưng NSUnderlyingErrorKey không phải lúc nào cũng được sử dụng - lỗi domain sẽ trả lại chỉ số bằng 0 (nil) cho phần lỗi tiềm tàng (underlying error).
1005NotInteractiveiOS 15Không có credentials khả dụng khi đang dùng preferImmediatelyAvailableCredentials. Đây là báo hiệu cho biết passkey không tồn tại. Tương tự như iOS, không có tín hiệu UI hiện lên.
1006MatchedExcludedCredentialiOS 18RP này đã có một loại passkey riêng trên thiết bị. Clean signal dùng để dò xem có passkey nào bị lập lại không - làm trống userInfo, không có NSUnderlyingErrorKey. Có thể thực hiện phân nhóm lỗi mà không cần gắn với string.
1007CredentialImportiOS 18.2Quá trình nạp credential thất bại.
1008CredentialExportiOS 18.2Quá trình xuất credential thất bại.
1009PreferSignInWithAppleiOS 26Người dùng chuộng sử dụng Sign in with Apple hơn dùng passkey. Xuất hiện lần đầu trong iOS 26.
1010DeviceNotConfiguredForPasskeyCreationiOS 26Thiết bị không có mật khẩu hoặc cấu hình iCloud Keychain. Lỗi được biết đến trong bộ mô phỏng trên iOS 26: hệ thống xuất 1010 mặc dù cấu hình vẫn đúng (không xuất hiện trên các thiết bị vật lý).

Sự khác biệt quan trọng nhất cho môi trường production: từ iOS 18, credential trùng lặp sẽ trả lại mã code riêng 1006 (MatchedExcludedCredential). Ở iOS 17 trở xuống, credential trùng lặp được giấu trong dòng code 1004 (Failed). Ở iOS 18+, sự khác biệt nằm ở cấu trúc (mã code khác nhau), chứ không phải là ở văn bản.

Các lỗi runtime phổ biến (tham khảo cấp độ nhật ký):

Những tin nhắn này xuất hiện trong phần localizedDescription hoặc trong userInfo cho từng trường hợp lỗi cụ thể. Hãy dùng chúng để tìm kiếm nhật ký và sửa lỗi, đừng dùng chúng để phân loại theo lập trình.

Message (English locale)Underlying codeGhi chú
Application with identifier <TeamID.BundleID> is not associated with domain X1004 (Failed)Phần phân quyền Các tên miền liên quan (Associated Domains) của app không hợp với bên cung cấp thứ 3. Khắc phục tệp apple-app-site-association trên máy chủ của bạn.
Couldn't communicate with a helper application.1004 (Failed)Hệ thống không phản hồi lại ứng dụng cung cấp loại chứng chỉ mở rộng. Chỉ là lỗi tạm thời - xin thử lại lần nữa.
Request already in progress for specified application identifier.1004 (Failed)Một bản sao của yêu cầu ASAuthorization đã được phát trong khi một cái khác vẫn đang chờ xử lý. Tình trạng chạy đua trong app (Race condition in the app).
Stolen Device Protection is enabled and biometry is required.1004 (Failed)Phần Mất Thiết bị An toàn (Stolen Device Protection) trên iOS 17 trở lên chặn xác minh sinh trắc học đối với những vị trí lạ. Developer không thể làm gì nhưng hãy báo cho người dùng.
(AuthenticationServicesCore.ASCABLEClient.ClientError error 2.)Phân miền riêng biệtXác thực giữa nhiều thiết bị với nhau (dạng lai/CABLE) qua quá trình thiết lập Bluetooth thất bại.
(AuthenticationServicesCore.ASCABLEClient.ClientError error 3.)Phân miền riêng biệtKết nối Bluetooth ở hai thiết bị khi xác thực thất bại.

Thông điệp nội địa hóa "không có thông tin xác thực" (code 1005):

Khi sử dụng preferImmediatelyAvailableCredentials và không có passkey nào, iOS sẽ trả về mã lỗi 1005 (NotInteractive) đi kèm với ngôn ngữ địa phương mà thiết bị sử dụng. Điều này là đặc quyền ở các ứng dụng native - các web browser không có loại tín hiệu này. Loại thông báo này luôn bắt đầu với phần The operation couldn't be completed. theo sau đó là thứ ngôn ngữ được thiết bị sử dụng:

Ngôn ngữTin nhắn
Chinese (Simplified)没有可用于登录的凭证。
VietnameseKhông có sẵn thông tin để đăng nhập.
Arabicلا تتوفر بيانات اعتماد لتسجيل الدخول.
SpanishNo hay ninguna credencial disponible para iniciar sesión.
Chinese (Traditional)沒有可用於登入的憑證。
Korean로그인을 위한 자격 증명이 없습니다.
French (Canada)Aucun identifiant disponible pour la connexion.
Portuguese (Brazil)Nenhuma credencial disponível para login.
French (France)Aucune information d'identification n'est disponible pour procéder à la connexion.
Thaiไม่มีข้อมูลประจำตัวสำหรับเข้าสู่ระบบ
ItalianNon ci sono credenziali disponibili per l'accesso.
DutchGeen inloggegevens beschikbaar.
Japaneseログイン用の資格情報がありません。
TurkishOturum açmak için kullanılabilecek kimlik bilgisi yok.

Các thiết bị ở chế độ ngôn ngữ tiếng Anh thường sẽ giải quyết lệnh "không có thông tin xác nhận" này tại cấp độ API trước khi cấu trúc ASAuthorization gửi lại một mã lỗi dạng địa phương hóa, đó là lý do mà không có phiên bản nào của tiếng Anh xuất hiện ở phần trên. Về mặc lập trình, mã lỗi code 1005 thay vì phân tích các đoạn chữ đó.

9.2 Android (Credential Manager API)#

Android hiển thị các lỗi liên quan đến passkey qua ứng dụng lập trình giao diện Credential Manager (androidx.credentials). Thông báo của nó bao gồm một thông báo chính cùng với nguyên do được giải thích vô cùng tỉ mỉ. Nếu so với iOS, Android lại cung cấp nhiều loại lỗi với cấu trúc hoàn chỉnh hơn và lý do cũng vô cùng dễ hiểu so với phần lý do của ứng dụng kia.

Người dùng từ chối thao tác & phân loại chứng chỉ:

LỗiLưu ý
User cancelled the operationNgười dùng tắt prompt về passkey. Nó có dạng giống với lỗi NotAllowedError. Chú ý: Ứng dụng quản lý xác minh trả về User canceled the request (phiên bản đánh vần bằng tiếng anh Mỹ) từ một đường đi dữ liệu khác.
Excluded credential matches existing credentialCó một loại chứng chỉ hiện đang tồn tại (tương tự như InvalidStateError). Loại thông báo này hoàn toàn khác biệt so với lời từ chối của người dùng.
No create options available.Không có nhà cung cấp xác nhận đạt tiêu chuẩn nào xử lý phần tạo yêu cầu này. Thường có nghĩa là do dịch vụ từ cửa hàng ứng dụng đã lỗi thời hoặc ứng dụng không hỗ trợ.

Lỗi về cấu hình và bảo mật:

LỗiLưu ý
Passkeys not supported for this appĐường dẫn kĩ thuật số (assetlinks.json) đã bị mất hoặc không thể dò ra được dấu vân tay của ứng dụng này (tương tự như lỗi SecurityError).
Https failed: respCode=301, url=https://<domain>/.well-known/assetlinks.jsonHệ thống báo lỗi cho tệp tin assetlinks.json vì nó trả lại dạng URL chuyển hướng thay vì URL HTTP 200. Android đòi hỏi yêu cầu file phải hoàn hảo.
The incoming request cannot be validatedỨng dụng Credential Manager không thể kiểm chứng tính chính xác của các đường link đã cung cấp.
RP ID cannot be validated.Phụ thuộc trên các thông tin ID đã cung cấp trong các ứng dụng WebAuthn nhưng nó lại không hợp với assetlinks.json.
Screen lock is missing.Máy không được thiết lập mã pin, kiểu màn hình khóa hay một chế độ đăng nhập bảo mật nào khác. Tính năng passkey đòi hỏi tính bảo mật cá nhân (Tương tự ConstraintError).
Cannot find an eligible account.Không có một tài khoản google khả dụng nào cho ứng dụng này (hiếm gặp, thường là đối với các loại tài khoản doanh nghiệp đã qua điều chỉnh từ người dùng).

Nền tảng hệ điều hành và lỗi của ứng dụng bên xác thực:

LỗiGhi chú
Unsuccessful result from folsom activity.Sự cố lỗi nội bộ trong Google Play. Dịch vụ "Folsom" là hệ thống thông số kỹ thuật (GMS) cho công đoạn cấp quyền đăng nhập (tạm thời - vui lòng thử lại).
Can't find the proper key to decrypt the private key from WebauthnCredentialSpecifics.Tính năng đồng bộ passkey đã được mở nhưng không được giải mã bởi máy chủ do ứng dụng quản lý mật khẩu Google không thể đồng bộ hóa khóa đồng bộ hóa giữa nhiều loại máy chủ khác nhau do không giải được chuỗi mật mã gốc (từ ứng dụng bên thứ 3) từ đó dẫn tới lỗi không mong muốn.
Operation was interrupted (cause: The UI was interrupted - please try again.)Giao diện của công cụ quản lý xác minh bị thao túng bởi một hoạt động (cuộc gọi đến, thay đổi màn hình khóa, ứng dụng trên máy) từ ứng dụng khác (tương đương với AbortError).
Unknown credential errorMột loại lỗi chung (chỉ xảy ra khi chưa thể phân loại hệ thống thông báo được). Thường thì sẽ là dạng lỗi tạm thời.
timeout (cause: Canceled)Quá trình quản lý chứng chỉ bị quá thời gian trước khi người dùng kịp hoàn thành phần bảo mật sinh trắc học.

10. Tổng hợp các thông tin trên: phân loại hệ thống các nhóm Lỗi#

Biểu đồ sau đây sẽ cho thấy cách tất cả các tầng được thảo luận ở trên - Hạ tầng (Infrastructure), Môi trường (Environment), Loại hoạt động (Operation type), Phân loại (Classification) và Phát hiện (Detection) - kết nối từ đầu đến cuối như thế nào. Đây là mô hình tinh thần bạn nên ghi nhớ khi thiết kế hệ thống theo dõi lỗi của mình.

Hiểu biết quan trọng nhất là: một tham số dạng error.name thô chỉ có thể hiểu được khi bạn xác định tầng lớp nào trong Environment tạo ra lỗi, Operation (Loại hoạt động) nào đang diễn ra và liệu lỗi là do được dự kiến hay không. Dưới đây là những cách để có thể lưu trữ lịch sử báo cáo lỗi.

11. Cần ghi log gì để các Lỗi có thể được debug#

Phần lớn các phân loại lỗi trong bài viết này có thể được thực hiện chỉ với tín hiệu ở phía máy khách. Một SDK quan sát front-end sẽ thu thập đủ bối cảnh cần thiết cho việc phân nhóm hầu hết các loại lỗi về chứng chỉ WebAuthn. Đây cũng là cách SDK quan sát của Corbado được thiết kế: tầng phía máy khách xử lý việc quy chụp lỗi, thời gian, ngữ cảnh hoạt động và phát hiện nền tảng. Nhật ký phía máy chủ bổ sung thêm lớp thứ hai cho các lỗi mà chỉ backend mới có thể thấy.

Yêu cầu chính: mỗi lần thử cần được kết nối từ đầu đến cuối một cách trơn tru. Một ID tương quan dùng chung (ví dụ: auth_flow_id) sẽ kết nối thông tin phía khách hàng cùng với kết quả kiểm tra phía server.

11.1 Các tín hiệu phía Máy khách (Frontend SDK)#

Tín hiệuLý do quan trọng
error.name + nhóm lý do chuẩn hóaLỗi trình duyệt thô + phân loại của bạn
Loại hoạt độngConditional UI, đăng nhập modal, tạo thủ công, conditional create, nối thêm tự động. CDA (Xác thực đa thiết bị) là một nhóm riêng phức tạp.
Ngữ cảnh Môi trường đầy đủHĐH + Phiên bản, Trình duyệt + Phiên bản, Thương hiệu/Mẫu Phần cứng, Cài đặt Authenticator (ví dụ: GPM đã kích hoạt?)
Ngữ cảnh Authenticator / trình quản lýExtension và nhà cung cấp bị gián đoạn
Thời gian từ khi bắt đầuBị từ chối ngay lập tức (<1s) so với người dùng hủy (1-15s) so với quá thời gian (30s+)
Trạng thái Mạng / Kết nốiLỗi mạng thường hiển thị thành lỗi phía máy khách. Theo dõi xem người dùng có bị offline và hàng đợi log để gửi ngay khi kết nối lại bình thường hay không.
Mã QR/hybrid UI có hiển thị khôngLỗi thiết bị so với lỗi xuyên nền tảng thiết bị
Correlation id (auth_flow_id)Để ghép với các log ở phía bên server

11.2 Các tín hiệu phía Máy chủ (Backend Verification)#

Sự cố kiểm tra server là một phần sau khi trình duyệt trả lại kết quả chứng chỉ và challenge đã được đăng kí. Đây nên là loại mã lỗi định dạng chi tiết cấu trúc thay vì lưu tại cùng một nơi với các loại tên ngoại lệ DOMException. Đọc phần: WebAuthn Server Implementation.

Tín hiệuLý do quan trọng
Sự bất đồng / lỗi quá hạn challengeThời gian cho một session hoặc sự lặp lại phiên có vấn đề
Lỗi Origin / RP ID mismatchThiết lập cấu hình hệ thống bao gồm nhiều tên miền bị sai
Cấu trúc / ID credential sai lệchBị gỡ khỏi hệ thống hay file bị lỗi. Một trường hợp điển hình nhất: thao tác truy cập sử dụng Conditional UI nhưng người dùng lại gỡ trên phía server. Hãy sử dụng Signal API để giữ mọi thông tin credential không bị sai.
Không đúng quy định về định dạng tênViệc phân loại, quy trình gán thông tin account có vấn đề
Dấu vết tương quan chung (auth_flow_id)Liên kết với thiết bị cá nhân để dò thêm phần gốc (context)

Nếu bạn cần tham khảo cho phễu thông tin (rút gọn hoặc loại bỏ các nhóm khách/đối tượng đã truy cập phễu thông qua các thao tác ở từng bước nhỏ): hãy vào trang passkey telemetry to understand drop-offs.

Substack Icon

Đăng ký Passkeys Substack để nhận tin mới nhất.

Đăng ký

Với dữ liệu này, kết luận trở nên đơn giản: hầu hết các "lỗi" đều có thể được xử lý bằng các bản sửa lỗi UX, bản sửa lỗi phạm vi hỗ trợ hoặc bản sửa lỗi cấu hình. Tuy nhiên, việc xây dựng và duy trì sự phân loại này cho riêng bạn đòi hỏi nhiều công sức liên tục.

12. Vượt xa khỏi Tên Lỗi: cách Corbado biến Lỗi thô thành Tín hiệu hành động#

Danh sách cần ghi log ở trên ghi lại các tín hiệu thô. Trong môi trường production ở quy mô lớn, chỉ riêng error.name là không đủ. Việc tự xây dựng hệ thống phân loại này đòi hỏi nỗ lực liên tục: các thông báo lỗi thay đổi theo từng bản phát hành trình duyệt và HĐH, các nhà cung cấp ứng dụng quản lý mật khẩu đưa ra các bản cập nhật làm thay đổi hành vi của buổi lễ và các dấu hiệu lỗi mới xuất hiện với mỗi đợt ra mắt tính năng.

12.1 Tại sao chỉ error.name là không đủ#

Cùng một NotAllowedError có thể mang 6 ý nghĩa khác nhau dựa trên ba tiêu chí mà trình duyệt không tách biệt cho bạn:

Tiêu chíNhững gì trình duyệt cung cấpNhững gì bạn thực sự cầnVí dụ
Ngữ cảnh hoạt độngNotAllowedErrorLà conditional UI, đăng nhập modal, tạo thủ công, conditional create, hay nối thêm tự động?Android trả về cùng một "unknown error" cho cả lượt đóng đăng nhập (dự kiến) và lỗi khi tạo (không dự kiến)
Thời gianKhông có dữ liệu độ dàiNgay lập tức (<1s) so với người dùng hủy (1-15s) so với quá thời gian (30s+)200ms = môi trường từ chối; 5s = người dùng thấy hộp thoại và hủy; 35s = quá thời gian
Nền tảng + authenticatorLỗi error.name chung chungHĐH, trình duyệt, phiên bản, trình quản lý thông tin xác thực cho mỗi lỗi"người dùng đã đóng hộp thoại" trên Chrome và "autofill không khả dụng" trên Safari đều xuất hiện dưới dạng NotAllowedError

12.2 Những gì SDK observability của Corbado nắm bắt#

Đây là vấn đề mà SDK observability của Corbado được xây dựng để giải quyết. Nó là một tích hợp frontend gọn nhẹ, nằm trên hệ thống triển khai passkey hiện có của bạn, hoạt động với bất kỳ máy chủ WebAuthn nào và bất kỳ IDP nào, và tự động phân loại từng lỗi WebAuthn dựa trên cả ba tiêu chí:

Khả năngChức năng thực hiện
Quy chụp lỗiNắm bắt HĐH, phiên bản HĐH, trình duyệt, phiên bản trình duyệt và trình xác thực sau mỗi lần thử
Chế độ hoạt độngKết nối từng lỗi với hoạt động cụ thể (conditional UI, đăng nhập modal, tạo thủ công, conditional create, nối thêm tự động) để cùng một NotAllowedError được quy về những nguyên nhân gốc rễ khác nhau
Đo thời gian từ lúc bắt đầuGhi nhận khoảng thời gian từ lúc khởi chạy để phân biệt giữa tình trạng từ chối ngay lập tức, người dùng hủy và quá thời gian mà không cần phỏng đoán
Phân loại lỗi thông minhSo khớp trên ngữ cảnh lỗi đầy đủ (không chỉ cái tên), chuẩn hóa xuyên suốt những Môi trường khác nhau (HĐH, Phần cứng, Cài đặt). Ưu tiên các nhóm lỗi phân biệt như CDA so với Local, và phân biệt lỗi Dự kiến (Người dùng hủy) so với lỗi Không dự kiến (Lỗi Hệ thống).
Sự phân mảnh ứng dụng mật khẩuPhát hiện khi các extension trình quản lý mật khẩu (Bitwarden, 1Password, LastPass) chặn ngang và trả về phản hồi phi tiêu chuẩn, phân biệt lỗi do extension so với lỗi nền tảng

Đây là tầng Quan sát (Observe): khả năng hiển thị chi tiết về những gì đang xảy ra mà không cần thay đổi quá trình triển khai của bạn.

12.3 Hai phương pháp debug: Top-Down và Bottom-Up#

Bảng điều khiển quản lý của Corbado hỗ trợ hai đường hướng điều tra:

Top-down (Từ dashboard đến nguyên nhân gốc rễ):

  1. Giám sát hai loại hiện tượng bất thường riêng biệt:
    • Lỗi Dự kiến tăng (Lệch chuẩn): Liệu một môi trường cụ thể (ví dụ: iOS 18.2 trên iPhone 15) có nhận thấy sự gia tăng đều đặn về số "người dùng hủy"? Điều này thường báo hiệu sự vướng mắc trong UX do bản cập nhật HĐH gây ra.
    • Lỗi Không dự kiến tăng (Đột biến): Một lỗi hoàn toàn mới hoặc lỗi tăng vọt đột ngột. Điều này thường báo hiệu về một sự thay đổi gây ra sự cố (cập nhật nội bộ trên stack IDP) hoặc sự thụt lùi ở một phiên bản trình duyệt mới.
  2. Ưu tiên theo tác động:
    • P1: Các vấn đề về Đăng nhập. Nếu tỷ lệ đăng nhập thành công giảm, cảnh báo ngay lập tức.
    • P2: Các vấn đề về Tạo. Theo dõi xu hướng, nhưng tránh đánh thức kỹ sư vào ban đêm vì sự tăng vọt "người dùng hủy" trong luồng tạo passkey.
  3. Phân tích sâu vào Môi trường: Dùng các tiêu chí chi tiết (Phần cứng, Cài đặt Auth) để cô lập xem sự cố này mang tính toàn cầu hay chỉ xảy ra cụ thể đối với các "thiết bị Samsung cài Android 14".
  4. Khắc phục: Nếu phát hiện một sự gia tăng đột biến nghiêm trọng, hãy chuẩn bị sẵn sàng một Công tắc ngắt (Kill Switch). Tự động hoặc thủ công vô hiệu hóa passkey cho môi trường đó và quay về dùng OTPs/Passwords để bảo vệ tỷ lệ đăng nhập trong khi bạn điều tra.

Bottom-up (Từ các loại lỗi đến hậu quả):

  1. Bắt đầu từ chế độ hiển thị phân loại lỗi. Xét duyệt lại các loại lỗi đã phân loại và quy mô khối lượng lỗi.
  2. Sàng lọc thông tin để lập ra danh sách bản đồ tổng quan các lỗi vừa xuất hiện (như là thông tin về một lỗi mới từ bản update vừa rồi).
  3. Các lỗi có tác động chéo đến thay đổi KPI và được xuất hiện dưới dạng thông báo trên dashboard của người dùng, nên tỷ lệ gặp loại lỗi cụ thể đột nhiên tăng đột biến sẽ được tự động liên kết với số liệu mà nó gây ảnh hưởng.

Tất cả các con đường đều dẫn tới: "top-down" báo cho bạn một thông tin xấu, trong khi "bottom-up" sẽ cho bạn biết về lý do đó. Hệ thống Phân tích Báo cáo từ AI hỗ trợ liên kết hai chiều với việc trả lời những câu hỏi từ các văn bản tự nhiên qua dữ liệu liên quan đến lỗi và việc cải thiện các số liệu đo lường.

Các nhóm muốn khắc phục hoặc tận dụng lại các thông tin này có thể thông qua Adopt - phần mềm cung cấp những chứng chỉ tình báo (passkey intelligence) giúp khởi tạo giao diện điều hướng tự động trên web, tối ưu hóa quá trình đăng ký chứng chỉ và sửa chữa thông tin lỗi trên passkey. Đối với những môi trường được quy định nghiêm ngặt hay có thông tin dữ liệu cực lớn, bản Enterprise có thể cấp quyền single-tenant hosting, cùng với SIEM integration và hệ thống tương thích cho PSD2.

Enterprise Icon

Nhận whitepaper passkey miễn phí cho doanh nghiệp.

Nhận miễn phí

13. Kết luận#

Các loại lỗi WebAuthn không phải là định kiến cuối cùng. Chúng chỉ là các gợi ý - và chúng sẽ chỉ trở nên quan trọng khi bạn có khả năng kết nối và tìm ra các loại dữ liệu hoạt động đang có trên trình duyệt, thời gian và quá trình xử lý của nền tảng mà bạn dùng.

  • Các tên lỗi WebAuthn phổ biến nhất trong môi trường production thường mang ý nghĩa gì? Phần lớn chúng sẽ được chia thành một vài nhóm nhỏ có liên quan đến: phân quyền quản lý quy trình sử dụng cho người dùng (NotAllowedError), dữ liệu ứng dụng (AbortError), ngữ cảnh/cấu hình bảo mật (SecurityError), hay trạng thái phân quyền (InvalidStateError, ConstraintError, DataError). Phần lớn quy mô trong môi trường sẽ nằm ở NotAllowedError, phần nhiều cũng chính là loại thông tin dễ dự kiến nhất (người dùng từ chối thao tác).
  • Làm cách nào để bạn phân biệt NotAllowedError? Dùng thời gian (bị từ chối ngay lập tức so với người dùng hủy bỏ và hết thời gian chờ), bộ quy chuẩn QR/công cụ hybrid (thông tin không khớp/khả dụng), dữ liệu thông tin về phân quyền kích hoạt (đặc biệt là iOS/Safari) và cách chúng vận hành (thông qua Conditional UI với hình thức modal log trên passkey hay là từ Conditional create). Đừng coi tất cả NotAllowedError là một loại lỗi hoạt động chung biệt.
  • Tại sao việc phân loại này lại quan trọng? Cùng một lỗi error.name nhưng khi ở trình thông báo của conditional UI lại là các mã code hoàn toàn khác biệt so với khi ở trình điều hướng cho conditional create, hoặc là phần thiết lập passkey truyền thống. Xác minh một log thuộc về loại nào cùng chung với loại lỗi đó là chìa khóa duy nhất chuyển NotAllowedError thành một loại lỗi có thể quản lý được.
  • Dữ kiện nào là loại quan trọng nhất để giúp cho việc debug trở nên dễ dàng hơn? Bao gồm error.name, loại hoạt động, thời lượng, loại hình đang sử dụng, và liệu có phải là hình thức ứng dụng/thiết lập lai nào đã được mở lên không, Hệ điều hành (hay trình duyệt, ứng dụng), cùng với thông tin kết nối (auth_flow_id) và các đoạn mã lỗi server rõ ràng.

Hai nguyên tắc vàng được áp dụng trên tất cả mọi loại lỗi: đừng bao giờ để người dùng nhìn thấy các lỗi thô của trình duyệt - luôn tạo cho họ một cách để dự phòng một cách rõ ràng nhất - và nên chia các cách quản lý cục bộ của nội bộ riêng và không được dính líu gì với hệ thống ngoại bộ khi có vấn đề. Các thiết lập và những phương pháp trên đôi khi phải tùy chỉnh cho phù hợp dựa theo phần cứng để tránh phát sinh ra nhiều hơn vì chúng sẽ gây rối hoặc bị can thiệp bởi mã bảo vệ của máy/ứng dụng bảo vệ của bên thứ 3 nên không có công thức cho mọi trường hợp. Ở những hệ thống quy mô lớn, liên kết các loại máy, những bản cập nhật của từng loại sẽ giúp cho việc phát hiện ra lỗi trở nên thường xuyên hơn. Nếu có thể, hãy dùng observability SDK được kết hợp từ nhiều thông số khác nhau thay vì làm mới từ ban đầu.

Corbado

Về Corbado

Corbado là Authentication Intelligence Platform dành cho các đội CIAM vận hành xác thực consumer ở quy mô lớn. Chúng tôi giúp bạn nhìn thấy điều mà log IDP và các công cụ analytics thông thường không thấy: những thiết bị, phiên bản OS, trình duyệt và trình quản lý credential nào hỗ trợ passkey, tại sao quá trình đăng ký không chuyển thành đăng nhập, luồng WebAuthn fail ở đâu, và khi nào một bản cập nhật OS hay trình duyệt làm hỏng đăng nhập một cách âm thầm — tất cả mà không cần thay thế Okta, Auth0, Ping, Cognito hay IDP nội bộ của bạn. Hai sản phẩm: Corbado Observe bổ sung observability cho passkey và mọi phương thức đăng nhập khác. Corbado Connect mang đến managed passkey với analytics tích hợp (song hành cùng IDP của bạn). VicRoads vận hành passkey cho hơn 5M người dùng với Corbado (kích hoạt passkey +80%). Trao đổi với chuyên gia Passkey

Những câu hỏi thường gặp#

Làm sao tôi có thể phân biệt giữa người dùng hủy lời nhắc passkey và thiết bị không có passkey?#

Trên web, các trình duyệt gộp cả hai trường hợp vào NotAllowedError vì lý do bảo mật quyền riêng tư, do đó bạn không thể phân biệt chúng trực tiếp. Hãy sử dụng thời gian làm yếu tố thay thế: lỗi xảy ra dưới 1 giây thường có nghĩa là do môi trường từ chối hoặc thiếu khả năng, trong khi lỗi từ 1-15 giây ám chỉ người dùng đã thấy và đóng hộp thoại. Trên các ứng dụng iOS và Android native, việc thiết lập preferImmediatelyAvailableCredentials trước khi gửi yêu cầu cung cấp cho bạn một tín hiệu "không có credentials" rõ ràng (mã 1005 trên iOS, GetCredentialRequest trên Android) trước khi giao diện người dùng hiển thị.

Tại sao tỷ lệ lỗi WebAuthn của tôi quá cao ngay cả khi passkey có vẻ đang hoạt động hiệu quả cho đa số người dùng?#

Trong các đợt triển khai passkey quy mô lớn được tối ưu hóa, trên 95% số lỗi WebAuthn được ghi lại đều là các trường hợp người dùng hủy theo dự kiến chứ không phải lỗi hệ thống. Việc theo dõi lượng error.name thô mà không tách biệt "người dùng đã đóng lời nhắc" khỏi các lỗi hỏng hóc thật sự sẽ thổi phồng các chỉ số đo lường lỗi và che đậy những sự cố đang ẩn chứa sau lượng NotAllowedError. Hãy phân chia số lượng dựa theo loại hoạt động, coi việc người dùng hủy giống như cách ta xử lý các tình huống đối với SecurityError, ConstraintErrorDataError.

Mã ASAuthorizationError 1005 trên iOS có nghĩa là gì và tôi nên sử dụng nó ra sao?#

Mã 1005 trên iOS (NotInteractive) có nghĩa là không có passkey nào khả dụng trên thiết bị khi preferImmediatelyAvailableCredentials được đặt trên ASAuthorizationController, và không có giao diện người dùng nào được hiển thị cho người dùng. Đây là tín hiệu sạch "không có passkey tồn tại trên thiết bị này" mà các trình duyệt web không thể cung cấp do những hạn chế về quyền riêng tư. Luôn phân loại dựa trên mã số này, thay vì dựa trên localizedDescription, bởi vì thông báo của Apple được bản địa hóa sang hơn 30 ngôn ngữ và có thể thay đổi theo từng phiên bản hệ điều hành.

Tôi nên xử lý NotAllowedError ra sao trong một luồng conditional create hoặc nối thêm passkey tự động?#

Trong quá trình conditional create, NotAllowedError, AbortErrorInvalidStateError đều là các kết quả nằm trong dự kiến và nên được bỏ qua một cách lặng lẽ thay vì hiển thị lên như một lỗi. NotAllowedError biểu thị khả năng tự động điền không có sẵn hoặc trang đã mất tiêu điểm, trong khi InvalidStateError có nghĩa là passkey đã tồn tại trong nhà cung cấp. Trước khi thử gọi, hãy sử dụng getClientCapabilities() để xác nhận hỗ trợ của conditionalCreate và kiểm tra trạng thái hiển thị của tài liệu nhằm tránh làm tăng số lượng lỗi của bạn.

Tôi nên ghi nhận ngữ cảnh nào cùng với error.name để có thể thực sự debug các lỗi WebAuthn?#

Hãy ghi nhận loại hoạt động (conditional UI, đăng nhập modal, tạo thủ công, conditional create hoặc nối thêm tự động), thời gian từ khi bắt đầu hoạt động đến khi xảy ra lỗi, phiên bản hệ điều hành, phiên bản trình duyệt, mẫu phần cứng, xem giao diện mã QR/hybrid có xuất hiện hay không và một ID tương quan để liên kết với nhật ký phía máy chủ. Các sự cố từ chối xác minh của máy chủ như không khớp challenge, chữ ký không hợp lệ và không tìm thấy credential nên được lưu lại dưới dạng các mã có cấu trúc rõ ràng, không được trộn lẫn với các tên DOMException ở phía máy khách. Nhờ kết hợp những tín hiệu này, bạn có thể phân loại phần lớn các lỗi vào các nhóm hành động rõ ràng mà không cần phải đoán mò.

Xem Corbado phù hợp thế nào với quá trình triển khai passkeys và stack xác thực hiện tại của bạn.

Khám phá Console

Chia sẻ bài viết này


LinkedInTwitterFacebook