Учетные данные FIDO, такие как ключи доступа, предназначены для замены паролей, но большинство из них также могут избавить пользователя от необходимости вводить имя пользователя. Это позволяет пользователям проходить аутентификацию, выбирая аккаунт из списка ключей доступа, которые у них есть для текущего сайта.
Ранние версии электронных ключей были разработаны как методы двухэтапной аутентификации и требовали идентификаторы потенциальных учетных данных, а значит, и ввода имени пользователя. Учетные данные, которые электронный ключ может найти, не зная их идентификаторов, называются обнаруживаемыми. Большинство учетных данных FIDO, создаваемых сегодня, являются обнаруживаемыми учетными данными, в частности ключами доступа, хранящимися в менеджере паролей или на современном электронном ключе.
Чтобы учетные данные создавались как ключи доступа (обнаруживаемые учетные данные), укажите residentKey и requireResidentKey при создании учетных данных.
Чтобы использовать обнаруживаемые учетные данные, полагающиеся стороны могут не указывать 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'– предпочтительно создать обнаруживаемые учетные данные, но допускается и создание необнаруживаемых.'discouraged'– сторона под ограничениями предпочитает создать невидимые учетные данные, но принимает и видимые.
requireResidentKey:
- Это свойство сохранено для обратной совместимости с WebAuthn Level 1, более старой версией спецификации. Задайте для этого параметра значение
true, если для параметраresidentKeyзадано значение'required'. В противном случае задайте значениеfalse.
allowCredentials
RP могут использовать 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;
// ...
}
Показывать форму автозаполнения ключа доступа
Описанный выше модальный селектор аккаунтов хорошо работает, если большинство пользователей используют ключи доступа и они доступны на локальном устройстве. Если у пользователя нет локальных ключей доступа, модальное диалоговое окно все равно появится и предложит пользователю предоставить ключ доступа с другого устройства. Пока вы переводите пользователей на ключи доступа, вы можете не показывать этот интерфейс тем, кто ещё не настроил ключ.
Вместо этого выбор ключа доступа может быть включен в подсказки автозаполнения для полей в традиционной форме входа, наряду с сохраненными именами пользователей и паролями. Таким образом, пользователь с ключами доступа может "заполнить" форму входа, выбрав свой ключ доступа, пользователи с сохраненными парами имя пользователя/пароль могут выбрать их, а пользователи, у которых нет ни того, ни другого, могут ввести свое имя пользователя и пароль.
Это идеальный вариант, если на платформе выполняется переход с паролей на ключи доступа.
Чтобы добиться такого пользовательского опыта, помимо передачи пустого массива в свойство allowCredentials или пропуска параметра, укажите mediation: 'conditional' в navigator.credentials.get() и аннотируйте поле ввода HTML username с помощью 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=&qu>ot;username webauthn" ...
Узнайте, как реализовать вход с помощью ключа доступа через автозаполнение формы, и выполните практическую работу Как реализовать ключи доступа с автозаполнением формы в веб-приложении.
Повторная аутентификация
В некоторых случаях, например при использовании ключей доступа для повторной аутентификации, идентификатор пользователя уже известен. В этом случае мы хотели бы использовать ключ доступа без показа браузером или ОС какой-либо формы выбора аккаунта. Для этого передайте список идентификаторов учетных данных в параметре allowCredentials.
В этом случае, если какие-либо из указанных учетных данных доступны локально, пользователю сразу предлагается разблокировать устройство. Если нет, пользователю будет предложено использовать другое устройство (телефон или электронный ключ), на котором хранятся действительные учетные данные.
Чтобы добиться такого пользовательского опыта, предоставьте список идентификаторов учетных данных для пользователя, выполняющего вход. Полагающаяся сторона должна иметь возможность запрашивать их, поскольку пользователь уже известен. Укажите идентификаторы учетных данных в виде объектов 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– идентификатор учетных данных открытого ключа, полученный проверяющей стороной при регистрации ключа доступа.type: обычно в этом поле указывается'public-key'.transports– подсказка о способах передачи данных, поддерживаемых устройством, на котором хранятся учетные данные. Браузеры используют ее, чтобы оптимизировать интерфейс, в котором пользователю предлагается подключить внешнее устройство. Если этот список указан, он должен содержать результат вызоваgetTransports()во время регистрации каждого набора учетных данных.
Сводка
Видимые учетные данные делают вход с помощью ключа доступа намного удобнее, поскольку пользователям не нужно вводить имя. Сочетание residentKey, requireResidentKey и allowCredentials позволяет реализовать на сайтах полагающихся сторон вход, который:
- Показать модальное окно выбора аккаунта.
- Показывать форму автозаполнения для ключей доступа.
- Повторная аутентификация.
Используйте обнаруживаемые учетные данные с умом. Это позволит вам создать удобный и привлекательный интерфейс для входа с помощью ключей доступа.