Перейти к содержимому
ecdsa.com
WebAuthn navigator.credentials.create() with excludeCredentialsWebAuthn и ключи доступа

Сообщение об ошибке

InvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.

Выше приведён текст Chromium; другие браузеры формулируют ошибку иначе. Неизменным остаётся только имя DOMException — `InvalidStateError`.

WebAuthn navigator.credentials.create() with excludeCredentials — DOMException InvalidStateError (Chromium)

Что это означает

Регистрация отклонена: на аутентификаторе уже есть учётные данные из списка `excludeCredentials`, который вы передали. Доверяющие стороны передают этот список именно для того, чтобы один и тот же аутентификатор не был зарегистрирован дважды для одного аккаунта, поэтому обычно это ожидаемый результат — у пользователя уже есть рабочий ключ доступа на этом устройстве. Браузеры сообщают об этой ошибке только после того, как пользователь взаимодействовал с аутентификатором.

Почему это происходит

Как исправить

  1. 1.

    Сообщите пользователю, что всё уже настроено

    Обрабатывайте InvalidStateError как «здесь уже есть ключ доступа» и подводите пользователя ко входу с этим ключом.

    js
    try {
      const credential = await navigator.credentials.create({ publicKey: options });
      await saveCredential(credential);
    } catch (e) {
      if (e.name === "InvalidStateError") {
        showMessage("This device already has a passkey for your account. You can use it to sign in.");
      } else if (e.name !== "NotAllowedError") {
        throw e;
      }
    }
  2. 2.

    Исключайте только учётные данные текущего пользователя

    Формируйте `excludeCredentials` из сохранённых идентификаторов учётных данных пользователя, который выполнил вход, и используйте для каждого аккаунта постоянный случайный `user.id`.

    js
    // Server: exclude only the CURRENT user's credentials (base64url ids from your database).
    const options = await generateRegistrationOptions({
      rpName: "Example",
      rpID,
      userName: user.email,
      userID: user.webauthnUserId,              // stable per user, never the email
      excludeCredentials: user.passkeys.map((p) => ({ id: p.credentialId, transports: p.transports })),
    });

Связанные ошибки

Открыть всю базу ошибок подписей