メインコンテンツへスキップ
Passkeys は、WebAuthn を使用して、フィッシング耐性のあるパスワードレス認証を実現します。複数のカスタムドメイン を使用する場合、WebAuthn’s セキュリティモデルにより、パスキー はドメインごとに登録されます。

カスタムドメインでパスキーがどのように機能するか

WebAuthn Relying Party ID (RP ID)

WebAuthn では、パスキー資格情報の有効範囲を定めるために Relying Party Identifier (RP ID) を使用します。RP ID は、次の点を決定します。
  • パスキーを使用できる場所: パスキーは、作成されたドメインに関連付けられます
  • セキュリティ境界: 許可されていないドメインでパスキーが使用されるのを防ぎます
  • ユーザー体験: ユーザーはカスタムドメインごとに個別にパスキーを登録する必要があります

ドメインごとの登録

複数のカスタムドメインがある場合、各ドメインにはそれぞれ固有の RP ID があります。つまり、次のことを意味します。
  • login.brand1.com で登録したパスキーは、login.brand2.com では使用できません
  • 異なるカスタムドメイン経由で認証するユーザーは、ドメインごとにパスキーを登録する必要があります
  • 各ドメインのパスキーは独立して管理されます

パスキーのユーザーエクスペリエンスを理解する

単一ブランド、単一ドメイン

セットアップ: 1 つのカスタムドメインで 1 つのブランドを提供 ユーザーエクスペリエンス:
  1. ユーザーが login.example.com にアクセスする
  2. ユーザーがパスキーを登録する
  3. ユーザーは以後、login.example.com 経由のすべてのログインでそのパスキーを使用できる
複雑さ: 低 - シンプルでわかりやすいパスキー体験

マルチブランド、個別ドメイン

セットアップ: 複数のブランドがあり、それぞれ独自のカスタムドメインを使用する ユーザー エクスペリエンス:
  1. ユーザーが login.brand1.com にアクセスしてパスキーを登録する
  2. 同じユーザーが後で login.brand2.com (別ブランド) にアクセスする
  3. 以前に登録したパスキーは使用できない
  4. ユーザーは login.brand2.com 用に新しいパスキーを登録する必要がある
複雑さ: 中程度 - ユーザーはブランドごとに別々のパスキーが必要 ベストプラクティス: ブランドごとに個別のパスキー登録が必要であることをユーザーに伝える

共通ドメインを使用するマルチテナント

セットアップ: 複数の顧客で、共有サービス向けに共通のカスタムドメインを使用します。 ユーザー体験:
  1. ほとんどのユーザーは共通ドメイン経由で認証します
  2. ユーザーは共通ドメインに対して一度だけパスキーを登録します
  3. パスキーはほとんどの認証シナリオで一貫して利用できます
  4. 特殊なケース (顧客固有のドメイン) では別途登録が必要です
複雑さ: 低~中 - ほとんどのユーザーで一貫した体験になります

設定

テナントでパスキーを有効にする

カスタムドメインでパスキーを使用する前に、パスキーが有効になっていることを確認してください。
  1. Auth0 Dashboard > Security > Multi-factor Auth に移動します
  2. WebAuthn with FIDO Security Keys を有効にします
  3. パスキー設定を構成します

パスキーのカスタムドメインを設定する

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

RP ID 設定を確認する

カスタムドメインの RP ID を確認するには、次の手順に従います。
  1. Auth0 Dashboard > ブランディング > カスタムドメイン に移動します
  2. カスタムドメインを選択します
  3. ドメインの詳細に、RP ID が表示されます

実装パターン

ドメインごとにパスキーの登録を促す

ユーザーが使用する各カスタムドメインでパスキーを登録するよう案内します。
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);
}

ドメインごとのパスキー登録を追跡する

ユーザーがどのドメインでパスキーを登録したかを保存します。
// 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);
  }
};
次に、アプリケーションで次を行います。
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();
    }
  }
}

ドメインごとの登録ページ

各カスタムドメイン専用の登録ページを作成します。
// 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);
  }
}

コンテキストに応じた登録プロンプト

ユーザーの行動に基づいて、パスキーの登録プロンプトを表示します。主な考慮事項:
  • ユーザーが登録プロンプトを閉じたタイミングを追跡する (localStorage に保存)
  • 複数回アクセスした後にプロンプトを表示するため、ユーザーメタデータの logins_count を確認する
  • 現在のドメインでパスキーがまだ登録されていないことを確認する
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 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>

ヘルプドキュメント

わかりやすいヘルプドキュメントを用意します。 FAQ の例: Q: なぜパスキーを再度設定する必要があるのですか? A: セキュリティ上、パスキーは特定のドメインに紐づいています。異なるポータル (例: Brand A と Brand B) からログインする場合は、それぞれでパスキーを設定する必要があります。これにより、パスキーは本来利用すべき場所でのみ機能し、アカウントの安全性が保たれます。 Q: パスキーごとに別のデバイスが必要ですか? A: いいえ。同じデバイス (スマートフォン、コンピューター、またはハードウェアキー) で、異なるドメインのパスキーを利用できます。各パスキーは、デバイスに保存される別個の認証情報にすぎません。

制限事項と留意点

現在の制限事項

制限事項影響回避策
ドメイン間でパスキーを共有できないユーザーはカスタムドメインごとに個別にパスキーを登録する必要があります認証の大半で共通ドメインを使用するか、各ドメインで登録するようユーザーを案内してください
ドメイン間でパスキーを移行できない新しいカスタムドメインに移行する場合は再登録が必要です移行を慎重に計画し、ユーザーに周知したうえで、再登録フローを提供してください
関連オリジン はまだサポートされていないサブドメイン間または関連するドメイン間でパスキーを共有できません今後のリリースでサポート予定です。現時点ではドメインごとの登録を使用してください
Auth0 は、指定したドメイン間でパスキーを共有できる WebAuthn の関連オリジンのサポートを予定しています。この機能により、次のことが可能になります。
  • パスキー用にドメインを「関連」として設定できるようになります
  • 関連として設定されている場合、ユーザーは login.brand1.com で登録したパスキーを login.brand2.com でも使用できるようになります
  • マルチブランド実装で、より柔軟に対応できるようになります
Status: GA 後のリリースで提供予定

移行シナリオ

単一のカスタムドメインから複数のカスタムドメインへの移行

移行前: パスキーが登録済みの単一カスタムドメイン 移行後: ブランドごとに異なる複数のカスタムドメイン 課題: 既存のパスキーは元のドメインでしか使用できない 移行アプローチ:
  1. 元のドメインを引き続き有効にする: 元のカスタムドメインを共通ドメインとして維持する
  2. 段階的に展開する: 新しいカスタムドメインを段階的に導入する
  3. ユーザーに通知する: 新しいドメインではパスキーを登録し直す必要があることをユーザーに知らせる
  4. 再登録フローを用意する: 新しいドメインでユーザーが簡単にパスキーを登録できるようにする
  5. 導入状況を監視する: ドメインごとのパスキー登録率を追跡する
通知テンプレート:
「ブランド別のログインページを導入します。既存のパスキーは引き続き [original domain] でご利用いただけます。新しいログインページにアクセスすると、そちらでもよりすばやくログインできるよう、パスキーの設定を求められます。」

カスタムドメイン間の移行

シナリオ: old-domain.com から new-domain.com への切り替え 課題: パスキーは移行できません 移行手順:
  1. 並行運用: 移行期間中は両方のドメインを同時に運用します
  2. 登録済みパスキーの検出: 古いドメインでパスキーを登録しているユーザーを把握します
  3. 再登録を促す: ユーザーが新しいドメイン経由でログインした際に、パスキーの再登録を促します
  4. 猶予期間: 一定の移行期間は古いドメインを有効のままにします
  5. 古いドメインの廃止: 移行が進んだら、古いドメインを廃止します
// 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);
    }
  }
};

テスト

ドメインごとのパスキー登録をテストする

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

テストの自動化

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);
  });
});

ベストプラクティス

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

トラブルシューティング

カスタムドメインでパスキーが機能しない

症状: ユーザーはパスキーを登録済みだが、使用できない 考えられる原因:
  • ユーザーが登録時とは異なるカスタムドメインを使用している
  • ブラウザーの互換性の問題
  • パスキーがデバイスから削除されている
解決方法:
  1. ユーザーが正しいカスタムドメインを使用していることを確認する
  2. ブラウザーが WebAuthn をサポートしているか確認する
  3. 必要に応じて、ユーザーにパスキーを再登録するよう案内する

複数回の登録にユーザーが混乱している

Symptoms: ドメインを切り替えると、「パスキーが機能しない」とユーザーから報告される Cause: ユーザーが、パスキーの登録がドメインごとに必要であることを理解していない Resolution:
  1. パスキーはドメインごとに登録が必要であることを明確に伝える
  2. ユーザーがどのドメインでパスキーを登録済みかを表示する
  3. ユーザーが新しいドメインにアクセスした際に登録を促す

関連情報