> ## 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) + "*****MASKED*****";
          }
          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>;
};

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

<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) {
    // ユーザーがパスキーを登録済みのDomainを追跡
    const enrolledDomains = event.user.app_metadata?.passkey_domains || [];

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

    // トークンにclaimを追加
    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` でも使用できるようになります
* マルチブランド実装で、より柔軟に運用できるようになります

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

<div id="migration-scenarios">
  ## 移行パターン
</div>

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

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

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

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

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

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">
  ### カスタムドメインでPasskeyが機能しない
</div>

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

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

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

**解決方法**:

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

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

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

**原因**: ユーザーがドメインごとの登録を理解していない

**解決策**:

1. ドメインごとのパスキーについて明確に案内する
2. ユーザーがどのドメインでパスキーを登録済みか表示する
3. ユーザーが新しいドメインを訪れた際に登録を促す

<div id="learn-more">
  ## 詳細はこちら
</div>

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