Условное создание ключа доступа (продвижение после входа)

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

Подходящий момент

Автоматическое создание ключа доступа (также известное как условное создание или скрытое предложение после входа) ДОЛЖНО запускаться только сразу после успешного полного входа с использованием пароля.

  • Не пытайтесь использовать условное создание для процессов без пароля (например, ссылок для входа, одноразовых кодов из 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';