本文へスキップ
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` は、ログイン中のユーザーについて保存されている認証情報 ID から作成し、`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 })),
    });

関連するエラー

署名エラーデータベースをすべて見る