> ## Documentation Index
> Fetch the complete documentation index at: https://translations.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Auth0 で複数のカスタムドメインを使用する場合に、パスキーと WebAuthn がどのように動作するかを説明します。

# 複数のカスタムドメインでのパスキー

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****マスク済み*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

[Passkeys](/ja/docs/secure/multi-factor-authentication/fido-authentication-with-webauthn) は、WebAuthn を使用して、フィッシング耐性のあるパスワードレス認証を実現します。[複数のカスタムドメイン](/ja/docs/customize/custom-domains/multiple-custom-domains) を使用する場合、WebAuthn's セキュリティモデルにより、パスキー はドメインごとに登録されます。

<div id="how-passkeys-work-with-custom-domains">
  ## カスタムドメインでパスキーがどのように機能するか
</div>

<div id="webauthn-relying-party-id-rp-id">
  ### WebAuthn Relying Party ID (RP ID)
</div>

WebAuthn では、パスキー資格情報の有効範囲を定めるために Relying Party Identifier (RP ID) を使用します。RP ID は、次の点を決定します。

* **パスキーを使用できる場所**: パスキーは、作成されたドメインに関連付けられます
* **セキュリティ境界**: 許可されていないドメインでパスキーが使用されるのを防ぎます
* **ユーザー体験**: ユーザーはカスタムドメインごとに個別にパスキーを登録する必要があります

<div id="per-domain-enrollment">
  ### ドメインごとの登録
</div>

複数のカスタムドメインがある場合、各ドメインにはそれぞれ固有の RP ID があります。つまり、次のことを意味します。

* `login.brand1.com` で登録したパスキーは、`login.brand2.com` では**使用できません**
* 異なるカスタムドメイン経由で認証するユーザーは、ドメインごとにパスキーを登録する必要があります
* 各ドメインのパスキーは独立して管理されます

<div id="understanding-the-passkey-user-experience">
  ## パスキーのユーザーエクスペリエンスを理解する
</div>

<div id="single-brand-single-domain">
  ### 単一ブランド、単一ドメイン
</div>

**セットアップ**: 1 つのカスタムドメインで 1 つのブランドを提供

**ユーザーエクスペリエンス**:

1. ユーザーが `login.example.com` にアクセスする
2. ユーザーがパスキーを登録する
3. ユーザーは以後、`login.example.com` 経由のすべてのログインでそのパスキーを使用できる

**複雑さ**: 低 - シンプルでわかりやすいパスキー体験

<div id="multi-brand-separate-domains">
  ### マルチブランド、個別ドメイン
</div>

**セットアップ**: 複数のブランドがあり、それぞれ独自のカスタムドメインを使用する

**ユーザー エクスペリエンス**:

1. ユーザーが `login.brand1.com` にアクセスしてパスキーを登録する
2. 同じユーザーが後で `login.brand2.com` (別ブランド) にアクセスする
3. 以前に登録したパスキーは使用できない
4. ユーザーは `login.brand2.com` 用に新しいパスキーを登録する必要がある

**複雑さ**: 中程度 - ユーザーはブランドごとに別々のパスキーが必要

**ベストプラクティス**: ブランドごとに個別のパスキー登録が必要であることをユーザーに伝える

<div id="multi-tenant-with-common-domain">
  ### 共通ドメインを使用するマルチテナント
</div>

**セットアップ**: 複数の顧客で、共有サービス向けに共通のカスタムドメインを使用します。

**ユーザー体験**:

1. ほとんどのユーザーは共通ドメイン経由で認証します
2. ユーザーは共通ドメインに対して一度だけパスキーを登録します
3. パスキーはほとんどの認証シナリオで一貫して利用できます
4. 特殊なケース (顧客固有のドメイン) では別途登録が必要です

**複雑さ**: 低～中 - ほとんどのユーザーで一貫した体験になります

<div id="configuration">
  ## 設定
</div>

<div id="enable-passkeys-for-your-tenant">
  ### テナントでパスキーを有効にする
</div>

カスタムドメインでパスキーを使用する前に、パスキーが有効になっていることを確認してください。

1. **Auth0 Dashboard** > **Security** > **Multi-factor Auth** に移動します
2. **WebAuthn with FIDO Security Keys** を有効にします
3. パスキー設定を構成します

<div id="configure-custom-domains-for-passkeys">
  ### パスキーのカスタムドメインを設定する
</div>

各カスタムドメインには、それぞれ専用の RP ID が自動的に設定されます。

* **RP ID の形式**: カスタムドメイン自体 (例: `login.example.com`)
* **追加設定は不要**: Auth0 により、検証済みの各カスタムドメインの RP ID が自動的に設定されます

<div id="verify-rp-id-configuration">
  ### RP ID 設定を確認する
</div>

カスタムドメインの RP ID を確認するには、次の手順に従います。

1. **Auth0 Dashboard** > **ブランディング** > **カスタムドメイン** に移動します
2. カスタムドメインを選択します
3. ドメインの詳細に、RP ID が表示されます

<div id="implementation-patterns">
  ## 実装パターン
</div>

<div id="prompt-for-passkey-enrollment-per-domain">
  ### ドメインごとにパスキーの登録を促す
</div>

ユーザーが使用する各カスタムドメインでパスキーを登録するよう案内します。

```javascript theme={null}
import { createAuth0Client } from '@auth0/auth0-spa-js';

async function setupPasskeyEnrollment() {
  const auth0 = await createAuth0Client({
    domain: 'login.example.com',
    clientId: 'YOUR_CLIENT_ID'
  });

  // ユーザーが認証済みかどうかを確認する
  const isAuthenticated = await auth0.isAuthenticated();

  if (isAuthenticated) {
    // このドメインにパスキーが登録されているかどうかを確認する
    const user = await auth0.getUser();

    if (!user.passkey_enrolled) {
      // パスキーの登録をユーザーに促す
      showPasskeyEnrollmentPrompt();
    }
  }
}

function showPasskeyEnrollmentPrompt() {
  // パスキーの登録を促すUIを表示する
  const banner = document.createElement('div');
  banner.innerHTML = `
    <div class="passkey-prompt">
      <p>Set up passkey for faster, more secure login on this site</p>
      <button onclick="enrollPasskey()">Set Up Passkey</button>
    </div>
  `;
  document.body.prepend(banner);
}
```

<div id="track-passkey-enrollment-by-domain">
  ### ドメインごとのパスキー登録を追跡する
</div>

ユーザーがどのドメインでパスキーを登録したかを保存します。

```javascript theme={null}
// Auth0 Action（Post-Login）内
exports.onExecutePostLogin = async (event, api) => {
  const domain = event.custom_domain?.domain;
  const authMethods = event.authentication?.methods || [];

  // ユーザーがパスキーで認証したかどうかを確認
  const usedPasskey = authMethods.some(method =>
    method.name === 'webauthn' || method.name === 'passkey'
  );

  if (usedPasskey) {
    // ユーザーがパスキーを登録済みのドメインを追跡
    const enrolledDomains = event.user.app_metadata?.passkey_domains || [];

    if (domain && !enrolledDomains.includes(domain)) {
      enrolledDomains.push(domain);
      api.user.setAppMetadata('passkey_domains', enrolledDomains);
    }

    // トークンにクレームを追加
    api.idToken.setCustomClaim('passkey_enrolled', true);
    api.idToken.setCustomClaim('passkey_domain', domain);
  } else {
    // ユーザーはパスキーを使用しなかった場合
    api.idToken.setCustomClaim('passkey_enrolled', false);
  }
};
```

次に、アプリケーションで次を行います。

```javascript theme={null}
async function checkPasskeyEnrollment() {
  const auth0 = await createAuth0Client({
    domain: window.CUSTOM_DOMAIN,
    clientId: 'YOUR_CLIENT_ID'
  });

  const isAuthenticated = await auth0.isAuthenticated();

  if (isAuthenticated) {
    const user = await auth0.getUser();
    const claims = await auth0.getIdTokenClaims();

    // 現在のドメインにパスキーが登録されているか確認する
    const passkeyEnrolledHere = claims.passkey_enrolled &&
                                 claims.passkey_domain === window.CUSTOM_DOMAIN;

    if (!passkeyEnrolledHere) {
      // このドメインへのパスキー登録をユーザーに促す
      promptPasskeyEnrollment();
    }
  }
}
```

<div id="domain-specific-enrollment-pages">
  ### ドメインごとの登録ページ
</div>

各カスタムドメイン専用の登録ページを作成します。

```javascript theme={null}
// Brand 1 の登録ページ
// URL: https://login.brand1.com/enroll-passkey

import { createAuth0Client } from '@auth0/auth0-spa-js';

async function enrollPasskeyForBrand1() {
  const auth0 = await createAuth0Client({
    domain: 'login.brand1.com',
    clientId: 'YOUR_CLIENT_ID'
  });

  try {
    // パスキー登録をトリガーする
    await auth0.loginWithPopup({
      authorizationParams: {
        acr_values: 'http://schemas.openid.net/pape/policies/2007/06/multi-factor',
        prompt: 'login'
      }
    });

    alert('Passkey enrolled successfully for Brand 1!');
  } catch (error) {
    console.error('Passkey enrollment failed:', error);
  }
}
```

<div id="contextual-enrollment-prompts">
  ### コンテキストに応じた登録プロンプト
</div>

ユーザーの行動に基づいて、パスキーの登録プロンプトを表示します。主な考慮事項:

* ユーザーが登録プロンプトを閉じたタイミングを追跡する (`localStorage` に保存)
* 複数回アクセスした後にプロンプトを表示するため、ユーザーメタデータの `logins_count` を確認する
* 現在のドメインでパスキーがまだ登録されていないことを確認する

```javascript theme={null}
async function shouldShowEnrollmentPrompt(auth0, customDomain) {
  const storageKey = `passkey_prompt_dismissed_${customDomain}`;

  // ユーザーが非表示にした場合は表示しない
  if (localStorage.getItem(storageKey)) return false;

  const claims = await auth0.getIdTokenClaims();
  const passkeyEnrolled = claims.passkey_enrolled &&
                          claims.passkey_domain === customDomain;

  if (passkeyEnrolled) return false;

  // 3回目のログイン後にプロンプトを表示する
  const user = await auth0.getUser();
  return (user.logins_count || 0) >= 3;
}
```

<div id="user-communication">
  ## ユーザーへの案内
</div>

<div id="inform-users-about-per-domain-enrollment">
  ### ドメインごとの登録についてユーザーに案内する
</div>

パスキーはドメインごとに異なることを、ユーザーに明確に伝えてください。

**メッセージ例**:

> "セキュリティ上、パスキーはログインポータルごとに異なります。利用するブランドごとのログインページごとに、別々にパスキーを設定する必要があります。"

**登録プロンプトの例**:

```html theme={null}
<div class="passkey-info-banner">
  <h3>Set up faster login with passkey</h3>
  <p>
    This passkey will work for login.example.com.
    If you use other login portals, you'll need to set up passkeys separately for each one.
  </p>
  <button onclick="enrollPasskey()">Set Up Passkey</button>
  <button onclick="dismissPrompt()">Not Now</button>
</div>
```

<div id="help-documentation">
  ### ヘルプドキュメント
</div>

わかりやすいヘルプドキュメントを用意します。

**FAQ の例**:

**Q: なぜパスキーを再度設定する必要があるのですか？**

A: セキュリティ上、パスキーは特定のドメインに紐づいています。異なるポータル (例: Brand A と Brand B) からログインする場合は、それぞれでパスキーを設定する必要があります。これにより、パスキーは本来利用すべき場所でのみ機能し、アカウントの安全性が保たれます。

**Q: パスキーごとに別のデバイスが必要ですか？**

A: いいえ。同じデバイス (スマートフォン、コンピューター、またはハードウェアキー) で、異なるドメインのパスキーを利用できます。各パスキーは、デバイスに保存される別個の認証情報にすぎません。

<div id="limitations-and-considerations">
  ## 制限事項と留意点
</div>

<div id="current-limitations">
  ### 現在の制限事項
</div>

<table class="table">
  <thead>
    <tr>
      <th><strong>制限事項</strong></th>
      <th><strong>影響</strong></th>
      <th><strong>回避策</strong></th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>ドメイン間でパスキーを共有できない</td>
      <td>ユーザーはカスタムドメインごとに個別にパスキーを登録する必要があります</td>
      <td>認証の大半で共通ドメインを使用するか、各ドメインで登録するようユーザーを案内してください</td>
    </tr>

    <tr>
      <td>ドメイン間でパスキーを移行できない</td>
      <td>新しいカスタムドメインに移行する場合は再登録が必要です</td>
      <td>移行を慎重に計画し、ユーザーに周知したうえで、再登録フローを提供してください</td>
    </tr>

    <tr>
      <td>関連オリジン はまだサポートされていない</td>
      <td>サブドメイン間または関連するドメイン間でパスキーを共有できません</td>
      <td>今後のリリースでサポート予定です。現時点ではドメインごとの登録を使用してください</td>
    </tr>
  </tbody>
</table>

<div id="related-origins-future-feature">
  ### 関連オリジン (将来提供予定の機能)
</div>

Auth0 は、指定したドメイン間でパスキーを共有できる WebAuthn の関連オリジンのサポートを予定しています。この機能により、次のことが可能になります。

* パスキー用にドメインを「関連」として設定できるようになります
* 関連として設定されている場合、ユーザーは `login.brand1.com` で登録したパスキーを `login.brand2.com` でも使用できるようになります
* マルチブランド実装で、より柔軟に対応できるようになります

**Status**: GA 後のリリースで提供予定

<div id="migration-scenarios">
  ## 移行シナリオ
</div>

<div id="migrating-from-single-custom-domain-to-multiple">
  ### 単一のカスタムドメインから複数のカスタムドメインへの移行
</div>

**移行前**: パスキーが登録済みの単一カスタムドメイン

**移行後**: ブランドごとに異なる複数のカスタムドメイン

**課題**: 既存のパスキーは元のドメインでしか使用できない

**移行アプローチ**:

1. **元のドメインを引き続き有効にする**: 元のカスタムドメインを共通ドメインとして維持する
2. **段階的に展開する**: 新しいカスタムドメインを段階的に導入する
3. **ユーザーに通知する**: 新しいドメインではパスキーを登録し直す必要があることをユーザーに知らせる
4. **再登録フローを用意する**: 新しいドメインでユーザーが簡単にパスキーを登録できるようにする
5. **導入状況を監視する**: ドメインごとのパスキー登録率を追跡する

**通知テンプレート**:

> 「ブランド別のログインページを導入します。既存のパスキーは引き続き \[original domain] でご利用いただけます。新しいログインページにアクセスすると、そちらでもよりすばやくログインできるよう、パスキーの設定を求められます。」

<div id="migrating-between-custom-domains">
  ### カスタムドメイン間の移行
</div>

**シナリオ**: `old-domain.com` から `new-domain.com` への切り替え

**課題**: パスキーは移行できません

**移行手順**:

1. **並行運用**: 移行期間中は両方のドメインを同時に運用します
2. **登録済みパスキーの検出**: 古いドメインでパスキーを登録しているユーザーを把握します
3. **再登録を促す**: ユーザーが新しいドメイン経由でログインした際に、パスキーの再登録を促します
4. **猶予期間**: 一定の移行期間は古いドメインを有効のままにします
5. **古いドメインの廃止**: 移行が進んだら、古いドメインを廃止します

```javascript theme={null}
// Auth0 Action 内
exports.onExecutePostLogin = async (event, api) => {
  const domain = event.custom_domain?.domain;
  const oldDomain = 'old-domain.com';
  const newDomain = 'new-domain.com';

  // ユーザーが旧ドメインでパスキーを登録済みか確認
  const hadOldPasskey = event.user.app_metadata?.passkey_domains?.includes(oldDomain);

  // ユーザーが新ドメインを使用しているが、パスキーがまだ登録されていない
  if (domain === newDomain && hadOldPasskey) {
    const newDomainPasskeys = event.user.app_metadata?.passkey_domains?.includes(newDomain);

    if (!newDomainPasskeys) {
      // 再登録を促すフラグを設定
      api.idToken.setCustomClaim('should_enroll_passkey', true);
      api.idToken.setCustomClaim('migrated_from', oldDomain);
    }
  }
};
```

<div id="testing">
  ## テスト
</div>

<div id="test-passkey-enrollment-per-domain">
  ### ドメインごとのパスキー登録をテストする
</div>

1. **テスト用カスタムドメインを設定する**: 開発テナントで複数のカスタムドメインを設定します
2. **登録フローをテストする**: 1 つのカスタムドメイン経由でパスキーを登録します
3. **分離を確認する**: そのパスキーが他のカスタムドメインでは機能しないことを確認します
4. **再登録をテストする**: 追加のドメインでパスキーを再登録します
5. **クロスブラウザテスト**: 異なるブラウザーやデバイスでテストします

<div id="automated-testing">
  ### テストの自動化
</div>

```javascript theme={null}
describe('Passkey Enrollment with Multiple Custom Domains', () => {
  it('should enroll passkey on domain 1', async () => {
    await navigateTo('https://login.brand1.com');
    await login();
    await enrollPasskey();
    expect(await isPasskeyEnrolled()).toBe(true);
  });

  it('should not have passkey on domain 2', async () => {
    await navigateTo('https://login.brand2.com');
    await login();
    expect(await isPasskeyEnrolled()).toBe(false);
  });

  it('should enroll separate passkey on domain 2', async () => {
    await navigateTo('https://login.brand2.com');
    await login();
    await enrollPasskey();
    expect(await isPasskeyEnrolled()).toBe(true);
  });
});
```

<div id="best-practices">
  ## ベストプラクティス
</div>

1. **共通のドメインを使用する**: パスキーの登録が必要なドメイン数を最小限に抑えるため、共通のカスタムドメインを使用します
2. **明確に伝える**: ドメインごとに登録が必要であることをユーザーに周知します
3. **戦略的にプロンプトを表示する**: ユーザーが十分に利用していることを確認してから (例: 3回以上ログインした後) 、登録プロンプトを表示します
4. **登録状況を追跡する**: どのユーザーがどのドメインでパスキーを登録したかを把握します
5. **サポートを提供する**: パスキー管理に関するわかりやすいドキュメントとサポートを提供します
6. **十分にテストする**: 本番環境へのデプロイ前に、すべてのカスタムドメインでパスキーフローを十分にテストします
7. **移行を計画する**: カスタムドメインを変更する場合は、ユーザーの再登録を考慮して計画します
8. **導入状況を監視する**: ドメインごとのパスキーの登録率と利用率を追跡します

<div id="troubleshooting">
  ## トラブルシューティング
</div>

<div id="passkey-not-working-on-custom-domain">
  ### カスタムドメインでパスキーが機能しない
</div>

**症状**: ユーザーはパスキーを登録済みだが、使用できない

**考えられる原因**:

* ユーザーが登録時とは異なるカスタムドメインを使用している
* ブラウザーの互換性の問題
* パスキーがデバイスから削除されている

**解決方法**:

1. ユーザーが正しいカスタムドメインを使用していることを確認する
2. ブラウザーが WebAuthn をサポートしているか確認する
3. 必要に応じて、ユーザーにパスキーを再登録するよう案内する

<div id="user-confused-about-multiple-enrollments">
  ### 複数回の登録にユーザーが混乱している
</div>

**Symptoms**: ドメインを切り替えると、「パスキーが機能しない」とユーザーから報告される

**Cause**: ユーザーが、パスキーの登録がドメインごとに必要であることを理解していない

**Resolution**:

1. パスキーはドメインごとに登録が必要であることを明確に伝える
2. ユーザーがどのドメインでパスキーを登録済みかを表示する
3. ユーザーが新しいドメインにアクセスした際に登録を促す

<div id="learn-more">
  ## 関連情報
</div>

* [複数のカスタムドメイン](/ja/docs/customize/custom-domains/multiple-custom-domains)
* [WebAuthn 仕様書](https://www.w3.org/TR/webauthn/)
* [Passkeys.dev](https://passkeys.dev/)
