Войдите в систему с помощью пароля через автозаполнение формы.

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

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

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

Зачем использовать автозаполнение форм для входа с помощью ключа доступа?

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

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

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

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

Использовать условный пользовательский интерфейс

Для эффективной поддержки пользователей как паролей, так и паролей включите пароли в предложения автозаполнения формы. Этот подход использует условный UI , функцию стандарта WebAuthn .

Пример выбора ключа доступа через автозаполнение форм.

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

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

Как работает аутентификация с помощью ключа доступа

Для аутентификации с помощью ключа доступа используется API WebAuthn .

Четыре компонента процесса аутентификации ключа доступа:

  • Бэкэнд : хранит данные учетной записи пользователя, включая открытый ключ.
  • Фронтенд : взаимодействует с браузером и извлекает необходимые данные из бэкенда.
  • Браузер : запускает ваш JavaScript и взаимодействует с API WebAuthn.
  • Поставщик ключа доступа : создает и хранит ключ доступа. Обычно это менеджер паролей, такой как Google Password Manager, или ключ безопасности.
Процесс аутентификации ключа доступа, демонстрирующий взаимодействие между интерфейсом, бэкэндом, браузером и поставщиком ключа доступа.
Полный процесс аутентификации ключа доступа.

Процесс аутентификации паролей выглядит следующим образом:

  1. Пользователь посещает страницу входа, и фронтенд запрашивает у бэкенда запрос на аутентификацию.
  2. Бэкэнд генерирует и возвращает запрос WebAuthn, связанный с учетной записью пользователя.
  3. Фронтенд вызывает navigator.credentials.get() с просьбой инициировать аутентификацию с использованием браузера .
  4. Браузер , взаимодействуя с поставщиком ключа доступа , предлагает пользователю выбрать ключ доступа (часто с помощью диалогового окна автозаполнения, которое запускается при наведении фокуса на поле входа) и подтвердить свою личность с помощью блокировки экрана устройства или биометрических данных.
  5. После успешной проверки пользователя поставщик ключа доступа подписывает запрос, и браузер возвращает полученные учетные данные открытого ключа (включая подпись) во внешний интерфейс .
  6. Фронтенд отправляет эти учетные данные бэкенду .
  7. Бэкенд проверяет подпись учетных данных с помощью сохраненного открытого ключа пользователя. Если проверка проходит успешно, бэкенд подписывает пользователя.

Аутентификация с помощью ключа доступа через автозаполнение форм

Чтобы инициировать аутентификацию ключа доступа с помощью автозаполнения форм, выполните условный вызов WebAuthn get при загрузке страницы входа. Этот вызов navigator.credentials.get() включает опцию mediation: 'conditional' .
Условный запрос к API WebAuthn navigator.credentials.get() не отображает пользовательский интерфейс немедленно. Вместо этого он ожидает в состоянии ожидания, пока пользователь не взаимодействует с запросом автозаполнения поля имени пользователя. Если пользователь выбирает ключ доступа, браузер разрешает ожидающее обещание с учетными данными для входа пользователя в систему, минуя традиционную отправку формы. Если пользователь выбирает пароль, обещание не разрешается, и продолжается стандартный поток входа с паролем.rm. Затем ответственность за вход пользователя в систему ложится на страницу.

Аннотировать поле ввода формы

Чтобы включить автозаполнение ключа доступа, добавьте атрибут autocomplete в поле input имени пользователя вашей формы. Включите как username , так и webauthn в качестве значений, разделенных пробелами.

<input type="text" name="username" autocomplete="username webauthn" autofocus>

Добавление autofocus в это поле автоматически запускает запрос на автозаполнение при загрузке страницы, немедленно отображая доступные пароли и ключи доступа.

Обнаружение особенностей

Перед вызовом условного вызова API WebAuthn проверьте следующее:

  • Браузер поддерживает WebAuthn с PublicKeyCredential .

Browser Support

  • Хром: 67.
  • Край: 18.
  • Firefox: 60.
  • Сафари: 13.

Source

Browser Support

  • Хром: 108.
  • Край: 108.
  • Firefox: 119.
  • Сафари: 16.

Source

В следующем фрагменте показано, как можно проверить, поддерживает ли браузер эти функции:

// Availability of window.PublicKeyCredential means WebAuthn is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isConditionalMediationAvailable) {  
  // Check if conditional mediation is available.  
  const isCMA = await PublicKeyCredential.isConditionalMediationAvailable();  
  if (isCMA) {  
    // Call WebAuthn authentication  
  }  
}  

Извлечение информации из бэкэнда

Ваш бэкэнд должен предоставить несколько опций фронтэнду для инициирования вызова navigator.credentials.get() . Эти опции обычно извлекаются как объект JSON из конечной точки на вашем сервере.

Ключевые свойства объекта параметров включают в себя:

  • challenge : сгенерированный сервером вызов в ArrayBuffer (обычно Base64URL, закодированный для JSON-транспорта). Это необходимо для предотвращения атак повторного воспроизведения. Ваш сервер должен генерировать новый вызов для каждой попытки входа и должен аннулировать его через короткое время или в случае неудачной попытки.
  • allowCredentials : Массив дескрипторов учетных данных. Передайте пустой массив . Это побуждает браузер вывести список всех учетных данных для указанного rpId .
  • userVerification : Указывает ваши предпочтения для проверки пользователя, например, требование блокировки экрана устройства. Значение по умолчанию и рекомендуемое значение — "preferred" . Возможные значения:

    • "required" : Проверка пользователя должна быть выполнена аутентификатором (например, PIN-кодом или биометрией). Операция завершается неудачей, если проверка не может быть выполнена.
    • "preferred" : Аутентификатор пытается выполнить проверку пользователя, но операция может быть выполнена и без нее.
    • "discouraged" : аутентификатор должен по возможности избегать проверки пользователя.
  • rpId : Ваш идентификатор проверяющей стороны, обычно домен вашего веб-сайта (например, example.com ). Это значение должно точно соответствовать rp.id , использованному при создании учетных данных ключа доступа.

Ваш сервер должен создать этот объект параметров. Значения ArrayBuffer (например, challenge ) должны быть закодированы в Base64URL для транспортировки JSON. На фронтенде после разбора JSON используйте PublicKeyCredential.parseRequestOptionsFromJSON() для преобразования объекта (включая декодирование строк Base64URL) в ожидаемый формат navigator.credentials.get() .

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

// Fetch an encoded PubicKeyCredentialRequestOptions from the server.
const _options = await fetch('/webauthn/signinRequest');

// Deserialize and decode the PublicKeyCredentialRequestOptions.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseRequestOptionsFromJSON(decoded_options);
...

Вызовите API WebAuthn с conditional флагом для аутентификации пользователя.

После подготовки объекта publicKeyCredentialRequestOptions (в примере кода ниже он называется options ) вызовите navigator.credentials.get() чтобы инициировать условную аутентификацию с помощью ключа доступа.

// To abort a WebAuthn call, instantiate an AbortController.
const abortController = new AbortController();

// Invoke WebAuthn to authenticate with a passkey.
const credential = await navigator.credentials.get({
  publicKey: options,
  signal: abortController.signal,
  // Specify 'conditional' to activate conditional UI
  mediation: 'conditional'
});

Ключевые параметры этого звонка:

  • publicKey : это должен быть объект publicKeyCredentialRequestOptions (в примере названный options ), который вы получили с сервера и обработали на предыдущем шаге.
  • signal : Передача сигнала AbortController (например, abortController.signal ) позволяет программно отменить запрос get() . Это полезно, когда вы хотите вызвать другой вызов WebAuthn.
  • mediation: 'conditional' : Это важный флаг, который делает вызов WebAuthn условным. Он сообщает браузеру, что нужно ждать взаимодействия с пользователем с запросом на автозаполнение, а не немедленно показывать модальный диалог.

Отправьте возвращенные учетные данные открытого ключа на сервер RP.

Если пользователь выбирает пароль и успешно подтверждает свою личность (например, используя блокировку экрана устройства), обещание navigator.credentials.get() разрешается. Это возвращает объект PublicKeyCredential в ваш frontend.

Обещание может быть отклонено по нескольким причинам. Вы должны обрабатывать эти ошибки в своем коде, проверяя свойство name объекта Error :

  • NotAllowedError : Пользователь отменил операцию или не был выбран ключ доступа.
  • AbortError : Операция была прервана, возможно, вашим кодом, использующим AbortController .
  • Другие исключения: Произошла непредвиденная ошибка. Браузер обычно показывает пользователю диалоговое окно с ошибкой.

Объект PublicKeyCredential содержит несколько свойств. Ключевые свойства, имеющие отношение к аутентификации, включают:

  • id : закодированный в base64url идентификатор аутентифицированного ключа доступа.
  • rawId : версия ArrayBuffer идентификатора учетных данных.
  • response.clientDataJSON : ArrayBuffer клиентских данных. Это поле содержит информацию, такую ​​как вызов и источник, который должен проверить ваш сервер.
  • response.authenticatorData : ArrayBuffer данных аутентификатора. Это поле включает информацию, такую ​​как идентификатор RP.
  • response.signature : ArrayBuffer, содержащий подпись. Это значение является ядром учетных данных, и ваш сервер должен проверить эту подпись, используя сохраненный открытый ключ для учетных данных.
  • response.userHandle : ArrayBuffer, содержащий идентификатор пользователя, предоставленный при регистрации ключа доступа.
  • authenticatorAttachment : Указывает, является ли аутентификатор частью клиентского устройства ( platform ) или внешним ( cross-platform ). cross-platform присоединение может произойти, если пользователь вошел в систему с телефона . В таких случаях рассмотрите возможность предложить им создать пароль на текущем устройстве для удобства в будущем.
  • type : это поле всегда имеет значение "public-key" .

Чтобы отправить этот объект PublicKeyCredential на ваш бэкэнд, сначала вызовите метод .toJSON() . Этот метод создает JSON-сериализуемую версию учетных данных, которая правильно обрабатывает преобразование свойств ArrayBuffer (таких как rawId , clientDataJSON , authenticatorData , signature и userHandle ) в закодированные строки Base64URL. Затем используйте JSON.stringify() для преобразования этого объекта в строку и отправки ее в теле вашего запроса на сервер.

...
// Encode and serialize the PublicKeyCredential.
const _result = credential.toJSON();
const result = JSON.stringify(_result);

// Encode and send the credential to the server for verification.  
const response = await fetch('/webauthn/signinResponse', {
  method: 'post',
  credentials: 'same-origin',
  body: result
});

Проверьте подпись

Когда ваш бэкэнд-сервер получает учетные данные открытого ключа, он должен проверить его подлинность. Это включает в себя:

  1. Анализ учетных данных.
  2. Поиск сохраненного открытого ключа, связанного с id учетных данных.
  3. Проверка полученной signature по сохраненному открытому ключу.
  4. Проверка других данных, таких как проблема и происхождение.

Мы рекомендуем использовать серверную библиотеку FIDO/WebAuthn для безопасной обработки этих криптографических операций. Вы можете найти библиотеки с открытым исходным кодом в репозитории awesome-webauthn GitHub .

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

Сигнализировать, если соответствующие учетные данные не найдены на бэкэнде

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

Вы можете использовать метод PublicKeyCredential.signalUnknownCredential() , часть API сигнала Webauthn , чтобы сообщить поставщику ключа доступа, что указанные учетные данные были удалены или не существуют. Вызовите этот статический метод на стороне клиента, если ваш сервер указывает (например, с помощью определенного кода статуса HTTP, например 404), что представленный идентификатор учетных данных неизвестен. Предоставьте идентификатор RP и неизвестный идентификатор учетных данных этому методу. Поставщик ключа доступа, если он поддерживает сигнал, должен удалить ключ доступа.

// Detect authentication failure due to lack of the credential
if (response.status === 404) {
  // Feature detection
  if (PublicKeyCredential.signalUnknownCredential) {
    await PublicKeyCredential.signalUnknownCredential({
      rpId: "example.com",
      credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
    });
  } else {
    // Encourage the user to delete the passkey from the password manager nevertheless.
    ...
  }
}

После аутентификации

В зависимости от того, как пользователь вошел в систему, мы предлагаем различные последовательности действий.

Если пользователь вошел в систему без ключа доступа

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

  • Обновите пароли до ключей доступа : используйте условное создание , функцию WebAuthn, которая позволяет браузеру автоматически создавать ключ доступа для пользователя после успешного входа с паролем. Это может значительно улучшить принятие ключа доступа за счет упрощения процесса создания. Узнайте, как это работает и как это реализовать, на странице Помогите пользователям более плавно принять ключи доступа
  • Вручную запрашивать создание ключа доступа : поощряйте пользователей создавать ключ доступа. Это может быть эффективно после того, как пользователь завершит более сложный процесс входа, например многофакторную аутентификацию (MFA). Однако избегайте чрезмерных запросов, которые могут быть навязчивыми для пользовательского опыта.

Чтобы узнать, как можно побудить пользователей создать ключ доступа и изучить другие полезные практики, ознакомьтесь с примерами в разделе «Передача ключей доступа пользователям» .

Если пользователь вошел в систему с помощью пароля

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

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

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

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

В этой ситуации рассмотрите возможность предложить пользователю создать новый ключ доступа на текущем устройстве. Это может избавить его от повторения процесса входа на разных устройствах в будущем. Чтобы определить, вошел ли пользователь с помощью ключа доступа на разных устройствах, проверьте свойство authenticatorAttachment учетных данных. Если его значение равно "cross-platform" это указывает на аутентификацию на разных устройствах. Если это так, объясните удобство создания нового ключа доступа и проведите пользователя через процесс создания.

Синхронизируйте данные ключа доступа с провайдером с помощью сигналов

Для обеспечения согласованности и лучшего пользовательского опыта ваша проверяющая сторона (RP) может использовать API сигналов WebAuthn для передачи обновлений учетных данных и информации о пользователе поставщику ключа доступа.

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

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

Дополнительную информацию о передовой практике обеспечения согласованности паролей см. в статье Обеспечение согласованности паролей с учетными данными на сервере с помощью API Signal .

Не просите второй фактор

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

Контрольный список

  • Разрешить пользователям входить в систему с помощью пароля через автозаполнение форм.
  • Сигнализировать, если соответствующие учетные данные ключа доступа не найдены на бэкэнде.
  • Предложите пользователям вручную создать пароль, если пользователь еще не создал его после входа в систему.
  • Автоматически создавать ключ доступа (условное создание) после того, как пользователь войдет в систему с паролем (и вторым фактором).
  • Запрос на создание локального ключа доступа, если пользователь вошел в систему с использованием ключа доступа для разных устройств.
  • После входа в систему или при возникновении изменений сообщите поставщику список доступных паролей и обновленные данные пользователя (имя пользователя, отображаемое имя).

Ресурсы