Подробное описание обнаруживаемых учетных данных

Хотя учетные данные FIDO, такие как ключи доступа, призваны заменить пароли, большинство из них также позволяют пользователю не вводить имя пользователя. Это дает пользователям возможность аутентифицироваться, выбрав учетную запись из списка ключей доступа, которые у них есть для текущего веб-сайта.

Более ранние версии ключей безопасности были разработаны как двухэтапные методы аутентификации и требовали идентификаторов потенциальных учетных данных, что, следовательно, предполагало ввод имени пользователя. Учетные данные, которые ключ безопасности может обнаружить, не зная их идентификаторов, называются обнаруживаемыми учетными данными. Большинство современных учетных данных FIDO являются обнаруживаемыми учетными данными; в частности, ключи доступа, хранящиеся в менеджере паролей или на современном ключе безопасности.

Чтобы ваши учетные данные были созданы как ключи доступа (доступные для обнаружения учетные данные), укажите residentKey и requireResidentKey при создании учетных данных.

Зависимые стороны (RP) могут использовать доступные учетные данные, опуская allowCredentials во время аутентификации с помощью пароля. В таких случаях браузер или система отображают пользователю список доступных паролей, идентифицируемых свойством user.name , установленным во время создания. Если пользователь выбирает один из них, значение user.id будет включено в результирующую подпись. Затем сервер может использовать это значение или возвращенный идентификатор учетных данных для поиска учетной записи вместо введенного имени пользователя.

В интерфейсах выбора учетной записи, подобных тем, что обсуждались ранее, никогда не отображаются учетные данные, которые невозможно обнаружить.

requireResidentKey и residentKey

Для создания пароля укажите в методе navigator.credentials.create() значения параметров authenticatorSelection.residentKey и authenticatorSelection.requireResidentKey , используя следующие параметры.

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' : Необходимо создать доступные учетные данные. Если это невозможно, возвращается NotSupportedError .
  • 'preferred' : RP предпочитает создавать доступные для обнаружения учетные данные, но принимает и недоступные.
  • 'discouraged' : Инициатор запроса предпочитает создать необнаруживаемые учетные данные, но принимает обнаруживаемые учетные данные.

requireResidentKey :

  • Это свойство сохранено для обеспечения обратной совместимости с WebAuthn Level 1, более старой версией спецификации. Установите значение true , если residentKey является 'required' , в противном случае установите значение false .

allowCredentials

Участники процесса аутентификации могут использовать allowCredentials в navigator.credentials.get() для управления процессом аутентификации с помощью пароля. Обычно существует три типа аутентификации с помощью пароля:

При наличии доступных учетных данных, точки доступа могут отображать модальное окно выбора учетной записи, позволяющее пользователю выбрать учетную запись для входа в систему, после чего следует подтверждение пользователя. Это подходит для аутентификации с помощью пароля, инициируемой нажатием кнопки, предназначенной для аутентификации с помощью пароля.

Для достижения такого пользовательского опыта следует опустить или передать пустой массив в параметр allowCredentials в 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;
  
  // ...
}

Показать автозаполнение формы ввода пароля

Описанный выше модальный диалог выбора учетной записи хорошо работает, если большинство пользователей используют пароли и имеют их на локальном устройстве. Для пользователя, у которого нет локальных паролей, модальное диалоговое окно все равно появляется и предлагает ввести пароль с другого устройства. При переходе пользователей на использование паролей, возможно, стоит избегать этого интерфейса для тех, кто еще не настроил пароль.

Вместо этого выбор пароля может быть интегрирован в подсказки автозаполнения полей в традиционной форме входа в систему, наряду с сохраненными именами пользователей и паролями. Таким образом, пользователь, имеющий пароли, может «заполнить» форму входа, выбрав свой пароль, пользователи с сохраненными парами имя пользователя/пароль могут выбрать их, а пользователи, не имеющие ни того, ни другого, по-прежнему могут ввести свое имя пользователя и пароль.

Такой пользовательский интерфейс идеально подходит в случае миграции RP с использованием смешанных паролей и ключей доступа.

Для достижения такого пользовательского опыта, помимо передачи пустого массива свойству allowCredentials или пропуска параметра, укажите mediation: 'conditional' в navigator.credentials.get() и аннотируйте поле ввода username в HTML-коде с помощью autocomplete="username webauthn" или поле ввода password с помощью autocomplete="password webauthn" .

Вызов метода navigator.credentials.get() не приведет к отображению какого-либо пользовательского интерфейса, но если пользователь сфокусируется на аннотированном поле ввода, все доступные пароли будут включены в варианты автозаполнения. Если пользователь выберет один из них, он пройдет обычную проверку разблокировки устройства, и только после этого промис, возвращаемый методом .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 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" ...>

Вы можете узнать, как создать такой пользовательский интерфейс, в разделе «Вход с помощью пароля через автозаполнение формы» , а также в практическом занятии « Реализация паролей с автозаполнением формы в веб-приложении» .

Повторная аутентификация

В некоторых случаях, например, при использовании паролей для повторной аутентификации, идентификатор пользователя уже известен. В этом случае нам нужно использовать пароль, не отображая в браузере или операционной системе какой-либо селектор учетной записи. Этого можно добиться, передав список идентификаторов учетных данных в параметре allowCredentials .

В этом случае, если какие-либо из указанных учетных данных доступны локально, пользователю сразу же предлагается разблокировать устройство. В противном случае пользователю предлагается предъявить другое устройство (телефон или ключ безопасности), содержащее действительные учетные данные.

Для обеспечения такого пользовательского опыта предоставьте список идентификаторов учетных данных для пользователя, выполняющего вход. Ответственный за вход (RP) должен иметь возможность запрашивать их, поскольку пользователь уже известен. Предоставьте идентификаторы учетных данных в виде объектов PublicKeyCredentialDescriptor в свойстве allowCredentials в navigator.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;
  
  // ...
}

Объект PublicKeyCredentialDescriptor состоит из:

  • id : Идентификатор учетных данных открытого ключа, полученных RP при регистрации пароля.
  • type : Обычно это поле имеет значение 'public-key' .
  • transports : Подсказка о поддерживаемых устройством, содержащим эти учетные данные, используемая браузерами для оптимизации пользовательского интерфейса, запрашивающего у пользователя подключение внешнего устройства. Этот список, если он предоставлен, должен содержать результат вызова getTransports() во время регистрации каждых учетных данных.

Краткое содержание

Доступные учетные данные значительно упрощают процесс входа в систему с помощью пароля, позволяя пользователям не вводить имя пользователя. Благодаря сочетанию residentKey , requireResidentKey и allowCredentials , пользователи могут добиться следующих результатов входа в систему:

  • Показать модальное окно выбора учетной записи.
  • Отобразить функцию автозаполнения формы ввода пароля.
  • Повторная аутентификация.

Разумно используйте доступные учетные данные. Таким образом, вы можете разработать сложные системы входа с помощью паролей, которые будут удобны для пользователей и повысят вероятность их использования.