Tạo khoá truy cập có điều kiện (Quảng bá sau khi đăng nhập)

Hướng dẫn này trình bày chi tiết cách tự động và âm thầm đăng ký khoá truy cập cho người dùng ngay sau khi họ đăng nhập thành công bằng mật khẩu, giúp giảm thiểu phiền hà và tăng tỷ lệ sử dụng khoá truy cập.

Thời điểm kích hoạt phù hợp

Tính năng tự động tạo khoá truy cập (còn gọi là Tạo có điều kiện hoặc quảng bá thầm lặng sau khi đăng nhập) CHỈ ĐƯỢC kích hoạt ngay sau khi đăng nhập thành công, đầy đủ bằng mật khẩu.

  • Đừng cố tạo có điều kiện cho các quy trình không cần mật khẩu (ví dụ: đường liên kết dùng một lần, SMS OTP hoặc liên kết danh tính).
  • Nếu cần xác thực đa yếu tố, bạn PHẢI đợi cho đến khi tất cả các yếu tố đều thành công trước khi bắt đầu tạo có điều kiện.
  • Đảm bảo phiên người dùng hợp lệ và đã được xác thực đang hoạt động trước khi gửi yêu cầu đến các điểm cuối tạo.

Các bước triển khai

1. Huỷ các thao tác tự động điền trước đó

Nếu trang đăng nhập sử dụng tính năng tự động điền biểu mẫu (Giao diện người dùng có điều kiện/Get), thì lệnh gọi lấy thông tin xác thực đang hoạt động phải bị huỷ để tránh xung đột trình duyệt.

  • Gọi abortController.abort() trên AbortController được đính kèm vào yêu cầu tự động điền navigator.credentials.get() đang chờ xử lý trước khi gọi navigator.credentials.create().

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

Xác định xem có thể sử dụng tính năng Tạo có điều kiện hay không bằng cách kiểm tra conditionalCreate bằng PublicKeyCredential.getClientCapabilities().

const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate) {
  // Conditional create is available
}

3. Tạo khoá truy cập bằng tính năng Tạo có điều kiện

  • Truyền mediation: 'conditional' trong các tuỳ chọn navigator.credentials.create(). Điều này báo hiệu cho trình duyệt xử lý quy trình tạo khoá truy cập một cách thầm lặng ở chế độ nền hoặc theo bối cảnh mà không hiển thị các hộp thoại phương thức gây phiền hà.
  • Điền mã thông tin xác thực khoá truy cập hiện có của người dùng vào excludeCredentials để tránh đăng ký khoá trùng lặp.

4. Xử lý lỗi thầm lặng

  • Gói lời nhắc tạo khoá truy cập (navigator.credentials.create) trong một khối try/catch. Bạn PHẢI phát hiện và bỏ qua các ngoại lệ thông thường dành cho người dùng (InvalidStateError, NotAllowedError, AbortError) mà không hiển thị bất kỳ giao diện người dùng lỗi nào cho người dùng.

5. Xác minh sự hiện diện của người dùng phía máy chủ

  • Điểm cuối xác minh phía máy chủ PHẢI nới lỏng yêu cầu về Sự hiện diện của người dùng (UP) (requireUserPresence: false) CHỈ khi xác minh thông tin xác thực do trình kích hoạt tạo có điều kiện tạo ra. Bạn phải luôn bật chế độ xác minh nghiêm ngặt về sự hiện diện đối với các hoạt động tạo rõ ràng theo tiêu chuẩn.

6. Xử lý khéo léo khi không xác minh được máy chủ

  • Nếu navigator.credentials.create() thành công nhưng quá trình tìm nạp xác minh máy chủ trả về phản hồi không hợp lệ (ví dụ: xác minh chữ ký không thành công), hãy gọi PublicKeyCredential.signalUnknownCredential() để ngăn thông tin xác thực mồ côi tồn tại trong trình cung cấp khoá truy cập.

Ví dụ về mã

// optionsFetch and registerVerifyFetch are app-defined server endpoint requests
import { optionsFetch, registerVerifyFetch } from './api.js';

async function triggerConditionalCreate(loginAbortController) {
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  if (capabilities.conditionalCreate !== true) {
    return; // Platform does not support conditional creation
  }

  // 1. Abort any active autofill conditional-get controllers to clear the WebAuthn pipeline
  loginAbortController.abort();

  // 2. Fetch creation options signaling the backend that this is a conditional request
  const creationOptionsJSON = await optionsFetch({ conditional: true });
  const publicKey = PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);

  let credential;
  try {
    // 3. Invoke silent credentials creation prompt
    credential = await navigator.credentials.create({ 
      publicKey,
      mediation: 'conditional' // Silent background creation mediation
    });
  } catch (e) {
    // 4. Silently swallow common WebAuthn browser exceptions
    if (['InvalidStateError', 'NotAllowedError', 'AbortError'].includes(e.name)) {
      return; 
    }
    console.error('Unexpected conditional create error:', e);
    return;
  }

  // 5. Server verification step using dedicated Try/Catch block
  let encodedResponse = credential.toJSON();
  try {
    const response = await registerVerifyFetch(encodedResponse);
    if (!response.ok) {
      // If the server verification fails, clean up using Signal API
      if (PublicKeyCredential.signalUnknownCredential) {
        await PublicKeyCredential.signalUnknownCredential({
          rpId, // RP ID must match the one defined on the server
          credentialId: encodedResponse.id
        });
      }
    }
  } catch (serverErr) {
    console.error('Verification network failure:', serverErr);
    if (PublicKeyCredential.signalUnknownCredential) {
      await PublicKeyCredential.signalUnknownCredential({
        rpId, // RP ID must match the one defined on the server
        credentialId: encodedResponse.id
      });
    }
  }
}

Chiến lược dự phòng

Dự phòng phát hiện tính năng khoá truy cập

Luôn cài đặt "webauthn-polyfills" và nhập vào ngữ cảnh. Hãy cân nhắc rằng miễn là PublicKeyCredential được hỗ trợ, thì PublicKeyCredential.getClientCapabilities cũng được hỗ trợ.

import 'webauthn-polyfills';

Dự phòng đồng bộ hoá API tín hiệu

WebAuthn Signal API (webauthn-signals) là một tính năng tối ưu hoá tăng dần dùng để đồng bộ hoá trình quản lý mật khẩu với trạng thái thông tin xác thực của máy chủ.

  • Trải nghiệm dự phòng: Được kiểm soát thông qua if (PublicKeyCredential.signalUnknownCredential). Nếu không được hỗ trợ, quá trình đồng bộ hoá xác minh trong nền sẽ được bỏ qua một cách linh hoạt mà không gửi ngoại lệ của trình duyệt.

Dự phòng chuyển đổi tuần tự JSON dễ dàng

Luôn cài đặt "webauthn-polyfills" và nhập vào ngữ cảnh. Hãy xem xét miễn là PublicKeyCredential được hỗ trợ, PublicKeyCredential.parseCreationOptionsFromJSON và PublicKeyCredential.prototype.toJSON cũng được hỗ trợ.

import 'webauthn-polyfills';