В этом руководстве подробно рассказывается, как автоматически и незаметно зарегистрировать ключ доступа для пользователя сразу после успешного входа с паролем, сводя к минимуму трудности и повышая уровень внедрения ключей доступа.
Подходящий момент
Автоматическое создание ключа доступа (также известное как условное создание или скрытое предложение после входа) ДОЛЖНО запускаться только сразу после успешного полного входа с использованием пароля.
- Не пытайтесь использовать условное создание для процессов без пароля (например, ссылок для входа, одноразовых кодов из SMS или федеративной идентификации).
- Если требуется многофакторная аутентификация, вы ДОЛЖНЫ дождаться успешного завершения всех факторов, прежде чем инициировать условное создание.
- Прежде чем отправлять запросы к конечным точкам создания, убедитесь, что сеанс пользователя действителен и аутентифицирован.
Руководство по настройке
1. Отменить предыдущие действия автозаполнения
Если на странице входа используется автозаполнение формы (условный интерфейс/Get), вызов получения активных учетных данных должен быть прерван, чтобы предотвратить конфликты в браузере.
- Вызовите
abortController.abort()дляAbortController, прикрепленного к ожидающему запросу автозаполненияnavigator.credentials.get(), перед вызовомnavigator.credentials.create().
2. Обнаружение функций
Чтобы узнать, доступна ли функция условного создания, проверьте conditionalCreate с помощью PublicKeyCredential.getClientCapabilities().
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate) {
// Conditional create is available
}
3. Как создать ключ доступа с помощью условного создания
- Передайте
mediation: 'conditional'в параметрахnavigator.credentials.create(). Это указывает браузеру, что процесс создания ключа доступа должен выполняться в фоновом режиме или в контексте, без показа навязчивых модальных диалоговых окон. - Заполните
excludeCredentialsидентификаторами существующих ключей доступа пользователя, чтобы избежать регистрации дубликатов.
4. Обработка ошибок без уведомления пользователя
- Оберните запрос на создание ключа доступа (
navigator.credentials.create) в блок try/catch. Вы ДОЛЖНЫ перехватывать и незаметно игнорировать типичные пользовательские исключения (InvalidStateError,NotAllowedError,AbortError), не показывая пользователю никаких ошибок в интерфейсе.
5. Проверка присутствия пользователя на стороне сервера
- Конечная точка проверки на стороне сервера ДОЛЖНА смягчать требование к присутствию пользователя (
requireUserPresence: false) ТОЛЬКО при проверке учетных данных, созданных триггером условного создания. Для стандартных материалов сексуального характера должна быть включена строгая проверка присутствия.
6. Обработка ошибок при проверке на сервере
- Если
navigator.credentials.create()выполняется успешно, но при получении подтверждения от сервера возвращается ошибка (например, не удается проверить подпись), вызовитеPublicKeyCredential.signalUnknownCredential(), чтобы предотвратить сохранение потерянных учетных данных у поставщика ключей доступа.
Пример кода
// optionsFetch and registerVerifyFetch are app-defined server endpoint requests
import { optionsFetch, registerVerifyFetch } from './api.js';
async function triggerConditionalCreate(loginAbortController) {
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate !== true) {
return; // Platform does not support conditional creation
}
// 1. Abort any active autofill conditional-get controllers to clear the WebAuthn pipeline
loginAbortController.abort();
// 2. Fetch creation options signaling the backend that this is a conditional request
const creationOptionsJSON = await optionsFetch({ conditional: true });
const publicKey = PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);
let credential;
try {
// 3. Invoke silent credentials creation prompt
credential = await navigator.credentials.create({
publicKey,
mediation: 'conditional' // Silent background creation mediation
});
} catch (e) {
// 4. Silently swallow common WebAuthn browser exceptions
if (['InvalidStateError', 'NotAllowedError', 'AbortError'].includes(e.name)) {
return;
}
console.error('Unexpected conditional create error:', e);
return;
}
// 5. Server verification step using dedicated Try/Catch block
let encodedResponse = credential.toJSON();
try {
const response = await registerVerifyFetch(encodedResponse);
if (!response.ok) {
// If the server verification fails, clean up using Signal API
if (PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId, // RP ID must match the one defined on the server
credentialId: encodedResponse.id
});
}
}
} catch (serverErr) {
console.error('Verification network failure:', serverErr);
if (PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId, // RP ID must match the one defined on the server
credentialId: encodedResponse.id
});
}
}
}
Резервные стратегии
Резервный вариант обнаружения функции ключей доступа
Всегда устанавливайте webauthn-polyfills и импортируйте его в контекст.
Учтите, что если поддерживается PublicKeyCredential, то поддерживается и PublicKeyCredential.getClientCapabilities.
import 'webauthn-polyfills';
Резервный вариант синхронизации Signal API
WebAuthn Signal API (webauthn-signals) – это прогрессивная оптимизация, которая используется для синхронизации менеджеров паролей с состоянием учетных данных на сервере.
- Резервный вариант. Доступен через
if (PublicKeyCredential.signalUnknownCredential). Если нет, то фоновая синхронизация проверки пропускается без ошибок браузера.
Простая резервная сериализация JSON
Всегда устанавливайте webauthn-polyfills и импортируйте его в контекст.
Если поддерживается PublicKeyCredential, то поддерживаются и PublicKeyCredential.parseCreationOptionsFromJSON, и PublicKeyCredential.prototype.toJSON.
import 'webauthn-polyfills';