Xác thực lại bằng khoá truy cập

Tính năng web: Xác thực web

Hướng dẫn tập trung vào delta này trình bày chi tiết cách triển khai quy trình xác thực nâng cao hoặc xác minh lại cho người dùng đã đăng nhập trước khi họ thực hiện các thay đổi nhạy cảm đối với tài khoản (ví dụ: cập nhật mật khẩu, chuyển tiền).

Kiến trúc Delta Flow

Không giống như quy trình xác thực thông thường, quy trình xác thực lại bằng khoá truy cập chỉ hiển thị lời nhắc trong hộp thoại khoá truy cập cho thông tin đăng nhập đã đăng ký trước của người dùng đã đăng nhập để ngăn chặn việc nhầm lẫn tài khoản hoặc giả mạo khoá truy cập trong các phiên hoạt động.

Phía máy chủ

Mức chênh lệch về số lượng lựa chọn được tạo

Tạo một điểm cuối điền sẵn các tham số thông tin xác thực được phép, dành riêng cho người dùng đang hoạt động và đã biết:

Giới hạn thông tin xác thực: Điền vào mảng tuỳ chọn allowCredentials bằng các bản ghi PublicKeyCredentialDescriptor cụ thể liên kết tất cả mã thông tin xác thực đã đăng ký cho người dùng đã đăng nhập. Nếu bạn để trống hoặc bỏ qua trường này, thông tin xác thực có thể phát hiện sẽ được sử dụng, vi phạm tính an toàn của phiên.

// Node.js step-up options generation example
router.post("/api/reauth/options", enforceActiveSession, async (req, res) => {
  const userPasskeys = await db.findCredentialsByUserId(req.user.id);

  const options = {
    challenge: serverGeneratedBase64UrlChallenge, // Random challenge stored in user session
    rpId: "example.com",
    // Enforce allowance strictly limited to the user's credentials list
    allowCredentials: userPasskeys.map((cred) => ({
      type: "public-key",
      id: cred.id,
      transports: cred.transports, // Speeds up resolution by indicating platform transports
    })),
  };
  return res.json(options);
});

Verification Endpoint Delta

Xác minh câu nhận định do ứng dụng trả về:

Xác minh quyền sở hữu tài khoản: Điểm cuối xác minh PHẢI xác minh rõ ràng rằng mã thông tin xác thực đã xác thực do ứng dụng trả về phân giải thành một bản ghi thông tin xác thực được lưu trữ có mã nhận dạng người dùng liên kết khớp chính xác với người dùng đã đăng nhập đang hoạt động (storedCredential.passkeyUserId === req.user.id). Nếu một khoá truy cập hợp lệ của một người dùng khác được trả về, thì quá trình xác thực PHẢI bị từ chối ngay lập tức.

Sự khác biệt về luồng phía máy khách

Ứng dụng chọn trong số hai giao diện xác thực lại tuỳ thuộc vào giao diện người dùng giao dịch:

A. Quy trình nút (Không có trường nhập dữ liệu)

Kích hoạt quy trình xác thực lại khi người dùng nhấn nút "Xác minh danh tính" hoặc "Tiếp tục giao dịch".

<button id="reauth-btn" data-testid="reauth-button">Confirm Transaction</button>
let reauthAbortController = new AbortController();

async function triggerButtonReauth() {
  // Abort any background suggestion flows to avoid passkey prompt collisions
  reauthAbortController.abort();
  reauthAbortController = new AbortController();

  const optionsResponse = await fetch("/api/reauth/options", {
    method: "POST",
  });
  const optionsJSON = await optionsResponse.json();
  const publicKey =
    PublicKeyCredential.parseRequestOptionsFromJSON(optionsJSON);

  try {
    const credential = await navigator.credentials.get({
      publicKey,
      signal: reauthAbortController.signal,
    });

    if (credential) {
      const encodedCredential = credential.toJSON();
      const verifyResponse = await fetch("/api/reauth/verify", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(encodedCredential),
      });

      if (verifyResponse.ok) {
        showTransactionSuccessUI();
      } else if (verifyResponse.status === 404 && PublicKeyCredential.signalUnknownCredential) {
        await PublicKeyCredential.signalUnknownCredential({
          rpId, // RP ID must match the one defined on the server
          credentialId: encodedCredential.id
        });
      }
    }
  } catch (err) {
    if (err.name === "NotAllowedError") {
      console.log("User cancelled reauthentication.");
    }
  }
}

document
  .getElementById("reauth-btn")
  .addEventListener("click", triggerButtonReauth);

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 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.parseRequestOptionsFromJSON và PublicKeyCredential.prototype.toJSON cũng được hỗ trợ.

import 'webauthn-polyfills';