Создайте систему входа, которая будет использовать ключи доступа, сохраняя при этом возможность использования существующих пользователей с паролями.
Ключи доступа заменяют пароли и делают учетные записи пользователей в Интернете более безопасными, простыми и легкими в использовании. Однако переход от аутентификации на основе пароля к аутентификации на основе ключа может усложнить работу пользователя. Использование автозаполнения форм для предложения ключей доступа может помочь создать унифицированный интерфейс.
Зачем использовать автозаполнение формы для входа с помощью ключа доступа?
С помощью ключа доступа пользователь может войти на веб-сайт, просто используя отпечаток пальца, лицо или PIN-код устройства.
В идеале не было бы пользователей с паролями, а процесс аутентификации мог бы быть таким же простым, как одна кнопка входа. Когда пользователь нажимает кнопку, появляется диалоговое окно выбора учетной записи, в котором пользователь может выбрать учетную запись, разблокировать экран для проверки и входа в систему.
Однако переход от аутентификации по паролю к аутентификации на основе пароля может оказаться сложной задачей. По мере того, как пользователи переходят на пароли, по-прежнему будут те, кто использует пароли, и веб-сайтам придется обслуживать оба типа пользователей. Не следует ожидать, что пользователи сами будут помнить, на каких сайтах они переключились на ключи доступа, поэтому просить пользователей заранее выбрать, какой метод использовать, было бы плохим UX.
Ключи доступа также являются новой технологией. Объяснить их и убедиться, что пользователям удобно ими пользоваться, может оказаться непростой задачей для веб-сайтов. Мы можем положиться на знакомый пользовательский опыт автозаполнения паролей для решения обеих проблем.
Условный интерфейс
Чтобы обеспечить эффективное взаимодействие с пользователем как с ключами доступа, так и с паролями, вы можете включать ключи доступа в предложения автозаполнения. Это называется условным пользовательским интерфейсом и является частью стандарта WebAuthn .
Как только пользователь нажимает на поле ввода имени пользователя, появляется диалоговое окно с предложением автозаполнения, в котором выделяются сохраненные ключи доступа вместе с предложениями автозаполнения пароля. Затем пользователь может выбрать учетную запись и использовать блокировку экрана устройства для входа.
Таким образом, пользователи смогут войти на ваш веб-сайт с помощью существующей формы, как будто ничего не изменилось, но с дополнительным преимуществом безопасности в виде ключей доступа, если они у них есть.
Как это работает
Для аутентификации с помощью ключа доступа вы используете WebAuthn API .
Четыре компонента в потоке аутентификации с помощью ключа доступа: пользователь:
- Бэкэнд : ваш внутренний сервер, на котором хранится база данных учетных записей, хранящая открытый ключ и другие метаданные о ключе доступа.
- Фронтенд : ваш фронтенд, который взаимодействует с браузером и отправляет запросы на выборку на бэкэнд.
- Браузер : браузер пользователя, в котором работает ваш Javascript.
- Аутентификатор : Аутентификатор пользователя, который создает и сохраняет ключ доступа. Это может быть то же устройство, что и браузер (например, при использовании Windows Hello), или другое устройство, например телефон.
![Схема аутентификации с помощью ключа доступа](https://web.dev/static/articles/passkey-form-autofill/image/passkey-authentication-diagram.png?authuser=1&hl=ru)
- Как только пользователь попадает на внешний интерфейс, он запрашивает у серверной части запрос на аутентификацию с помощью ключа доступа и вызывает
navigator.credentials.get()
чтобы инициировать аутентификацию с помощью ключа доступа. Это возвращаетPromise
. - Когда пользователь помещает курсор в поле входа, браузер отображает диалоговое окно автозаполнения пароля, включая ключи доступа. Диалоговое окно аутентификации появляется, если пользователь выбирает ключ доступа.
- После того как пользователь подтверждает свою личность с помощью блокировки экрана устройства, обещание разрешается, и учетные данные открытого ключа возвращаются во внешний интерфейс.
- Внешний интерфейс отправляет учетные данные открытого ключа на серверную часть. Серверная часть сверяет подпись с открытым ключом соответствующей учетной записи в базе данных. Если это удалось, пользователь войдет в систему.
Аутентификация с помощью ключа доступа через автозаполнение формы
Когда пользователь хочет войти в систему, вы можете выполнить условный вызов WebAuthn get
, чтобы указать, что ключи доступа могут быть включены в предложения автозаполнения. Условный вызов API navigator.credentials.get()
WebAuthn не отображает пользовательский интерфейс и остается в режиме ожидания до тех пор, пока пользователь не выберет учетную запись для входа в систему из предложений автозаполнения. Если пользователь выбирает ключ доступа, браузер выполнит обещание с учетными данными, а не заполняет форму входа. В этом случае ответственность за вход пользователя лежит на странице.
Поле ввода формы аннотации
При необходимости добавьте атрибут autocomplete
в поле input
имени пользователя. Добавьте username
и webauthn
в качестве токенов, чтобы он мог предлагать ключи доступа.
<input type="text" name="username" autocomplete="username webauthn" ...>
Обнаружение функций
Прежде чем вызывать условный вызов API WebAuthn, проверьте:
- Браузер поддерживает WebAuthn с
PublicKeyCredential
.
- Браузер поддерживает условный пользовательский интерфейс WebAuthn с помощью
PublicKeyCredenital.isConditionalMediationAvailable()
.
// 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
}
}
Получить вызов с сервера RP
Получите запрос от RP-сервера, необходимый для вызова navigator.credentials.get()
:
-
challenge
: вызов, сгенерированный сервером в ArrayBuffer. Это необходимо для предотвращения атак повторного воспроизведения. Обязательно создавайте новый запрос при каждой попытке входа в систему и игнорируйте его по истечении определенного времени или после того, как попытка входа не прошла проверку. Считайте это токеном CSRF. -
allowCredentials
: Массив допустимых учетных данных для этой аутентификации. Передайте пустой массив, чтобы пользователь мог выбрать доступный ключ доступа из списка, отображаемого браузером. userVerification
: указывает, является ли проверка пользователя с помощью блокировки экрана устройства"required"
,"preferred"
или"discouraged"
. По умолчанию установлено значение"preferred"
, что означает, что аутентификатор может пропустить проверку пользователя. Установите для этого параметра значение"preferred"
или опустите это свойство.
Вызов WebAuthn API с conditional
флагом для аутентификации пользователя.
Вызовите navigator.credentials.get()
, чтобы начать ожидание аутентификации пользователя.
// To abort a WebAuthn call, instantiate an `AbortController`.
const abortController = new AbortController();
const publicKeyCredentialRequestOptions = {
// Server generated challenge
challenge: ****,
// The same RP ID as used during registration
rpId: 'example.com',
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal,
// Specify 'conditional' to activate conditional UI
mediation: 'conditional'
});
-
rpId
: идентификатор RP — это домен, и веб-сайт может указать либо свой домен, либо регистрируемый суффикс. Это значение должно соответствовать идентификатору rp.id, использованному при создании ключа доступа.
Не забудьте указать mediation: 'conditional'
чтобы сделать запрос условным.
Отправьте возвращенные учетные данные открытого ключа на сервер RP.
После того как пользователь выбирает учетную запись и дает согласие на использование блокировки экрана устройства, обещание разрешается, возвращая объект PublicKeyCredential
во внешний интерфейс RP.
Обещание может быть отклонено по нескольким причинам. Вам необходимо обрабатывать ошибки соответствующим образом, в зависимости от свойства name
объекта Error
:
-
NotAllowedError
: пользователь отменил операцию. - Другие исключения : Произошло что-то неожиданное. Браузер показывает пользователю диалоговое окно с ошибкой.
Объект учетных данных открытого ключа содержит следующие свойства:
-
id
: идентификатор аутентифицированного ключа доступа в кодировке Base64url. -
rawId
: версия идентификатора учетных данных ArrayBuffer. -
response.clientDataJSON
: ArrayBuffer данных клиента. Это поле содержит такую информацию, как запрос и источник, который серверу RP необходимо будет проверить. -
response.authenticatorData
: ArrayBuffer данных аутентификатора. Это поле содержит такую информацию, как идентификатор RP. -
response.signature
: ArrayBuffer подписи. Это значение является основой учетных данных и должно быть проверено на сервере. -
response.userHandle
: ArrayBuffer, содержащий идентификатор пользователя, установленный во время создания. Это значение можно использовать вместо идентификатора учетных данных, если серверу необходимо выбрать значения идентификаторов, которые он использует, или если серверная часть желает избежать создания индекса по идентификаторам учетных данных. -
authenticatorAttachment
: возвращаетplatform
, когда эти учетные данные получены с локального устройства. В противном случаеcross-platform
, особенно когда пользователь использовал телефон для входа в систему . Если пользователю нужно было использовать телефон для входа, рассмотрите возможность предложить ему создать ключ доступа на локальном устройстве. -
type
: в этом поле всегда установлено значение"public-key"
.
Если вы используете библиотеку для обработки объекта учетных данных с открытым ключом на сервере RP, мы рекомендуем вам отправить весь объект на сервер после его частичного кодирования с помощью base64url.
Проверьте подпись
Когда вы получите учетные данные открытого ключа на сервере, передайте их в библиотеку FIDO для обработки объекта.
Найдите соответствующий идентификатор учетных данных с помощью свойства id
(если вам нужно определить учетную запись пользователя, используйте свойство userHandle
, которое представляет собой user.id
, указанный вами при создании учетных данных). Посмотрите, можно ли проверить signature
учетных данных с помощью сохраненного открытого ключа. Для этого мы рекомендуем использовать серверную библиотеку или решение вместо написания собственного кода. Библиотеки с открытым исходным кодом вы можете найти в репозитории Awesome-webauth на GitHub .
После проверки учетных данных с помощью соответствующего открытого ключа войдите в систему.
Следуйте более подробным инструкциям в разделе Аутентификация с помощью пароля на стороне сервера.