Tìm hiểu chuyên sâu về thông tin xác thực có thể phát hiện

Mặc dù thông tin đăng nhập FIDO (chẳng hạn như khoá truy cập) sẽ thay thế mật khẩu, nhưng hầu hết các thông tin này cũng có thể giúp người dùng không phải nhập tên người dùng. Thao tác này cho phép người dùng xác thực bằng cách chọn một tài khoản trong danh sách khoá truy cập mà họ có cho trang web hiện tại.

Các phiên bản trước của khoá bảo mật được thiết kế dưới dạng phương thức xác thực 2 bước và đòi hỏi mã của thông tin đăng nhập có thể có, do đó yêu cầu nhập tên người dùng. Thông tin đăng nhập mà một khoá bảo mật có thể tìm thấy mà không cần biết mã đó được gọi là thông tin xác thực có thể phát hiện. Hầu hết thông tin đăng nhập FIDO được tạo hiện nay đều là thông tin đăng nhập có thể phát hiện được, nhất là các khoá truy cập được lưu trữ trong một trình quản lý mật khẩu hoặc trên một khoá bảo mật hiện đại.

Để đảm bảo người dùng có thể tìm thấy thông tin đăng nhập của bạn, hãy chỉ định residentKeyrequireResidentKey khi tạo khoá truy cập.

Các bên tin cậy (RP) có thể sử dụng thông tin xác thực có thể phát hiện được bằng cách bỏ qua allowCredentials trong quá trình xác thực khoá truy cập. Trong những trường hợp như vậy, trình duyệt hoặc hệ thống sẽ cho người dùng thấy danh sách các khoá truy cập có sẵn, được xác định bằng thuộc tính user.name được đặt tại thời điểm tạo. Nếu người dùng chọn một giá trị, giá trị user.id sẽ được đưa vào chữ ký thu được. Sau đó, máy chủ có thể sử dụng mã đó hoặc mã thông tin xác thực được trả về để tra cứu tài khoản thay vì tên người dùng đã nhập.

Giao diện người dùng của bộ chọn tài khoản (giống như các giao diện đã thảo luận trước đó) tuyệt đối không hiển thị thông tin đăng nhập không phát hiện được.

requireResidentKeyresidentKey

Để tạo thông tin xác thực có thể phát hiện, hãy chỉ định authenticatorSelection.residentKeyauthenticatorSelection.requireResidentKey trên navigator.credentials.create() bằng các giá trị được biểu thị như sau.

async function register () {
  // ...

  const publicKeyCredentialCreationOptions = {
    // ...
    authenticatorSelection: {
      authenticatorAttachment: 'platform',
      residentKey: 'required',
      requireResidentKey: true,
    }
  };

  const credential = await navigator.credentials.create({
    publicKey: publicKeyCredentialCreationOptions
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;

  // ...
}

residentKey:

  • 'required': Phải tạo thông tin xác thực có thể phát hiện. Nếu không thể tạo hàm, hàm sẽ trả về NotSupportedError.
  • 'preferred': Bên bị hạn chế muốn tạo thông tin xác thực có thể tìm thấy nhưng chấp nhận một thông tin xác thực không phát hiện được.
  • 'discouraged': Bên bị hạn chế muốn tạo một thông tin xác thực không phát hiện được nhưng chấp nhận một thông tin xác thực có thể phát hiện.

requireResidentKey:

  • Thuộc tính này được giữ lại để đảm bảo khả năng tương thích ngược từ WebAuthn Cấp 1 (phiên bản cũ hơn của thông số kỹ thuật). Thiết lập thành true nếu residentKey'required', nếu không, hãy thiết lập thành false.

allowCredentials

Bên bị hạn chế có thể sử dụng allowCredentials trên navigator.credentials.get() để kiểm soát quy trình xác thực khoá truy cập. Thường có ba loại trải nghiệm xác thực bằng khoá truy cập:

Nhờ thông tin đăng nhập mà bên bị hạn chế có thể phát hiện, bên bị hạn chế có thể hiển thị bộ chọn tài khoản phương thức để người dùng chọn tài khoản cần đăng nhập, sau đó là xác minh người dùng. Cách này phù hợp với quy trình xác thực khoá truy cập được khởi tạo bằng cách nhấn một nút dành riêng cho quy trình xác thực khoá truy cập.

Để có được trải nghiệm người dùng này, hãy bỏ qua hoặc truyền một mảng trống đến tham số allowCredentials trong navigator.credentials.get().

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // You can omit `allowCredentials` as well:
    allowCredentials: []
  };

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

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}

Hiện một biểu mẫu tự động điền khoá truy cập

Bộ chọn tài khoản phương thức được mô tả ở trên sẽ hoạt động hiệu quả nếu hầu hết người dùng đều sử dụng khoá truy cập và có sẵn khoá đó trên thiết bị cục bộ. Đối với người dùng không có khoá truy cập cục bộ, hộp thoại phương thức vẫn xuất hiện và sẽ đề nghị người dùng trình bày khoá truy cập từ một thiết bị khác. Trong khi chuyển đổi người dùng sang khoá truy cập, bạn nên tránh sử dụng giao diện người dùng đó cho những người dùng chưa thiết lập.

Thay vào đó, lựa chọn khoá truy cập có thể được thu gọn thành lời nhắc tự động điền cho các trường trong biểu mẫu đăng nhập truyền thống, cùng với tên người dùng và mật khẩu đã lưu. Bằng cách này, người dùng có khoá truy cập có thể "điền" vào biểu mẫu đăng nhập bằng cách chọn khoá truy cập, người dùng đã lưu cặp tên người dùng/mật khẩu có thể chọn các cặp tên người dùng/mật khẩu đó, còn người dùng chưa có khoá truy cập vẫn có thể nhập tên người dùng và mật khẩu.

Trải nghiệm người dùng này tối ưu khi bên bị hạn chế sử dụng kết hợp mật khẩu và mã xác thực.

Để có được trải nghiệm người dùng này, ngoài việc truyền một mảng trống đến thuộc tính allowCredentials hoặc bỏ qua tham số, hãy chỉ định mediation: 'conditional' trên navigator.credentials.get() và chú thích trường nhập dữ liệu HTML username bằng autocomplete="username webauthn" hoặc trường nhập dữ liệu password bằng autocomplete="password webauthn".

Lệnh gọi đến navigator.credentials.get() sẽ không khiến giao diện người dùng xuất hiện. Tuy nhiên, nếu người dùng tập trung vào trường nhập dữ liệu có chú giải, thì mọi khoá truy cập hiện có sẽ được đưa vào các tuỳ chọn tự động điền. Nếu người dùng chọn một tuỳ chọn cài đặt, họ sẽ thực hiện quy trình xác minh mở khoá thiết bị thông thường, và chỉ sau đó lời hứa trả về của .get() mới giải quyết bằng một kết quả. Nếu người dùng không chọn khoá truy cập, thì lời hứa sẽ không bao giờ được giải quyết.

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // You can omit `allowCredentials` as well:
    allowCredentials: []
  };

  const cred = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal,
    // Specify 'conditional' to activate conditional UI
    mediation: 'conditional'
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}
<input type="text" name="username" autocomplete="username webauthn" ...>

Bạn có thể tìm hiểu cách xây dựng trải nghiệm người dùng này tại lớp học lập trình Đăng nhập bằng khoá truy cập thông qua tính năng tự động điền biểu mẫu, cũng như lớp học lập trình Triển khai khoá truy cập bằng tính năng tự động điền biểu mẫu trong ứng dụng web.

Xác thực lại

Trong một số trường hợp, chẳng hạn như khi dùng khoá truy cập để xác thực lại, giá trị nhận dạng của người dùng đã được xác định. Trong trường hợp này, chúng ta muốn sử dụng khoá truy cập mà trình duyệt hoặc hệ điều hành không hiển thị bất kỳ dạng bộ chọn tài khoản nào. Bạn có thể thực hiện việc này bằng cách truyền danh sách mã thông tin xác thực vào tham số allowCredentials.

Trong trường hợp đó, nếu có sẵn bất kỳ thông tin đăng nhập có tên nào trên máy, người dùng sẽ được nhắc mở khoá thiết bị ngay lập tức. Nếu không, người dùng sẽ được nhắc xuất trình một thiết bị khác (điện thoại hoặc khoá bảo mật) có thông tin đăng nhập hợp lệ.

Để có được trải nghiệm người dùng này, hãy cung cấp danh sách mã thông tin xác thực cho người dùng đăng nhập. Bên bị hạn chế truy vấn được vì người dùng này đã biết. Cung cấp mã thông tin xác thực dưới dạng đối tượng PublicKeyCredentialDescriptor trong thuộc tính allowCredentialsnavigator.credentials.get().

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // Provide a list of PublicKeyCredentialDescriptors:
    allowCredentials: [{
      id: ****,
      type: 'public-key',
      transports: [
        'internal',
        'hybrid'
      ]
    }, {
      id: ****,
      type: 'public-key',
      transports: [
        'internal',
        'hybrid'
      ]
    }, ...]
  };

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

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}

Đối tượng PublicKeyCredentialDescriptor bao gồm:

  • id: Mã của thông tin xác thực khoá công khai mà RP đã nhận được khi đăng ký khoá truy cập.
  • type: Trường này thường là 'public-key'.
  • transports: Gợi ý về phương thức truyền tải được thiết bị có thông tin xác thực này hỗ trợ, được các trình duyệt sử dụng để tối ưu hoá giao diện người dùng yêu cầu người dùng đưa ra thiết bị bên ngoài. Danh sách này, nếu được cung cấp, sẽ chứa kết quả của lệnh gọi getTransports() trong quá trình đăng ký từng thông tin xác thực.

Tóm tắt

Thông tin đăng nhập bằng khoá truy cập giúp trải nghiệm đăng nhập bằng khoá truy cập thân thiện hơn với người dùng vì họ có thể bỏ qua bước nhập tên người dùng. Với sự kết hợp của residentKey, requireResidentKeyallowCredentials, các bên bị hạn chế có thể có được trải nghiệm đăng nhập mà:

  • Hiển thị bộ chọn tài khoản phương thức.
  • Hiện một biểu mẫu tự động điền khoá truy cập.
  • Xác thực lại.

Sử dụng thông tin xác thực có thể tìm được một cách khôn ngoan. Nhờ đó, bạn có thể thiết kế trải nghiệm đăng nhập tinh vi bằng khoá truy cập sao cho người dùng thấy liền mạch và dễ tương tác hơn.