パスキーの条件付き作成(ログイン後のプロモーション)

このガイドでは、パスワードベースのログインが成功した直後に、ユーザーのパスキーを自動的にサイレント登録し、摩擦を最小限に抑えてパスキーの導入を促進する方法について詳しく説明します。

適切なトリガー モーメント

パスキーの自動作成(条件付き作成またはログイン後のサイレント プロモーションとも呼ばれます)は、パスワードを使用した完全なログインが成功した直後にのみトリガーする必要があります。

  • パスワードレス フロー(マジックリンク、SMS OTP、ID 連携など)で条件付き作成を試みないでください。
  • 多要素認証が必要な場合は、すべての要素が成功するまで待ってから、条件付き作成を開始する必要があります。
  • 作成エンドポイントにリクエストを送信する前に、有効な認証済みユーザー セッションがアクティブであることを確認します。

実装の手順

1. 以前の自動入力アクションを中止する

ログインページでフォームの自動入力(条件付き UI / 取得)を使用している場合は、ブラウザの競合を防ぐために、アクティブな認証情報の取得呼び出しを中止する必要があります。

  • navigator.credentials.create() を呼び出す前に、保留中の navigator.credentials.get() 自動入力リクエストにアタッチされている AbortController で abortController.abort() を呼び出します。

2. 特徴検出

PublicKeyCredential.getClientCapabilities() を指定して conditionalCreate を確認し、条件付き作成が利用可能かどうかを判断します。

const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate) {
  // Conditional create is available
}

3. 条件付き作成でパスキーを作成する

  • navigator.credentials.create() オプション内で mediation: 'conditional' を渡します。これにより、ブラウザはパスキー作成フローをバックグラウンドでサイレントに処理するか、コンテキストに応じて処理し、邪魔なモーダル ダイアログを表示しません。
  • excludeCredentials にユーザーの既存のパスキー認証情報 ID を入力して、重複する鍵が登録されないようにします。

4. サイレント エラー処理

  • パスキー作成プロンプト(navigator.credentials.create)を try / catch ブロックでラップします。InvalidStateError、NotAllowedError、AbortError などの一般的なユーザー向け例外をキャッチし、エラー UI をユーザーに表示せずに無視しなければなりません。

5. サーバーサイドのユーザー プレゼンス検証

  • サーバーサイド認証エンドポイントは、条件付き作成トリガーによって生成された認証情報を検証する場合にのみ、ユーザー プレゼンス(UP)要件(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';