Хотя учетные данные FIDO, такие как ключи доступа, предназначены для замены паролей, большинство из них также могут освободить пользователя от ввода имени пользователя. Это позволяет пользователям проходить аутентификацию, выбирая учетную запись из списка паролей, которые у них есть для текущего веб-сайта.
Более ранние версии ключей безопасности были разработаны как методы двухэтапной аутентификации и требовали идентификаторов потенциальных учетных данных, что требовало ввода имени пользователя. Учетные данные, которые ключ безопасности может найти, не зная их идентификаторов, называются обнаруживаемыми учетными данными. Большинство учетных данных FIDO, созданных сегодня, являются доступными для обнаружения; особенно ключи доступа, хранящиеся в менеджере паролей или на современном ключе безопасности.
Чтобы гарантировать, что ваши учетные данные создаются как ключи доступа (обнаруживаемые учетные данные), укажите residentKey
и requireResidentKey
при создании учетных данных.
Проверяющие стороны (RP) могут использовать обнаруживаемые учетные данные, опуская allowCredentials
во время проверки подлинности с помощью ключа доступа. В этих случаях браузер или система отображают пользователю список доступных ключей доступа, определяемых свойством user.name
, установленным во время создания. Если пользователь выберет один из них, значение user.id
будет включено в результирующую подпись. Затем сервер может использовать этот или возвращенный идентификатор учетных данных для поиска учетной записи вместо введенного имени пользователя.
Пользовательские интерфейсы выбора учетной записи, подобные тем, которые обсуждались ранее, никогда не отображают недоступные для обнаружения учетные данные.
requireResidentKey
и residentKey
Чтобы создать ключ доступа, authenticatorSelection.residentKey
authenticatorSelection.requireResidentKey
в navigator.credentials.create()
со значениями, указанными ниже.
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'
: RP предпочитает создавать необнаружимые учетные данные, но принимает обнаруживаемые учетные данные.
requireResidentKey
:
- Это свойство сохранено для обратной совместимости с WebAuthn Level 1, более старой версией спецификации. Установите для этого параметра значение
true
, еслиresidentKey
является'required'
, в противном случае установите значениеfalse
.
allowCredentials
RP могут allowCredentials
в navigator.credentials.get()
для управления процессом аутентификации с помощью ключа доступа. Обычно существует три типа аутентификации с помощью пароля:
- Показать модальный выбор учетной записи
- Показать автозаполнение формы ключа доступа
- Повторная аутентификация
Показать модальный выбор учетной записи
Благодаря обнаруживаемым учетным данным RP могут отображать модальный выбор учетной записи, позволяющий пользователю выбрать учетную запись для входа в систему с последующей проверкой пользователя. Это подходит для процесса аутентификации пароля, инициируемого нажатием кнопки, предназначенной для аутентификации пароля.
Чтобы добиться такого пользовательского опыта, опустите или передайте пустой массив в 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
RP могут обеспечить такие возможности входа в систему, которые:
- Показать модальный выбор учетной записи.
- Показать автозаполнение формы ключа доступа.
- Повторная аутентификация.
Используйте обнаруживаемые учетные данные с умом. Таким образом, вы можете разработать сложные способы входа в систему с помощью пароля, которые пользователи найдут удобными и с большей вероятностью будут взаимодействовать.