> ## 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 SPA SDK を使用して、プレーンな JavaScript を使うシングルページアプリケーション（SPA）に Auth0 を統合し、認証を追加して、ユーザープロファイル情報を表示する方法を説明します。

# JavaScript アプリケーションにログインを追加

export const HowToSchema = () => <script type="application/ld+json">
    {'{"@context":"https://schema.org","@type":"HowTo"}'}
  </script>;

export const CreateInteractiveApp = ({placeholderText = "Auth0", appType = "regular_web", allowedCallbackUrls = ["localhost:3000"], allowedLogoutUrls = ["localhost:3000"], allowedOriginUrls = ["localhost:3000"]}) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [storeReady, setStoreReady] = useState(false);
  const [displayForm, setDisplayForm] = useState(true);
  useEffect(() => {
    const init = () => setStoreReady(true);
    if (window.rootStore) {
      window.rootStore.clientStore.setSelectedClient(null);
      window.rootStore.clientStore.setSelectedClientSecret(undefined);
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
    };
  }, []);
  useEffect(() => {
    if (!storeReady) return;
    const disposer = autorun(() => {
      const rootStore = window.rootStore;
      setIsAuthenticated(rootStore.sessionStore.isAuthenticated);
    });
    return () => {
      disposer();
    };
  }, [storeReady]);
  if (!storeReady || typeof window === "undefined" || !displayForm) {
    return <></>;
  }
  const login = () => {
    const baseUrl = window.rootStore.config.apiBaseUrl;
    const returnTo = encodeURIComponent(window.location.href);
    window.location.href = `${baseUrl}/auth/user/login?returnTo=${returnTo}`;
  };
  const Card = ({className = "", children}) => {
    return <div className={`
          flex border rounded-2xl
          border-gray-950/10 dark:border-white/10
          py-3.5 px-4 gap-2
          text-sm text-gray-900 dark:text-gray-200
          ${className}
        `}>
        {children}
      </div>;
  };
  const Button = ({children, ...props}) => {
    return <button className="bg-[--button-primary] text-[--foreground-inverse] px-[1.125rem] py-1.5 rounded-lg font-medium" {...props}>
        {children}
      </button>;
  };
  const CreateApplicationForm = () => {
    const [name, setName] = useState("");
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState("");
    const handleSubmit = async () => {
      if (!name.trim()) {
        setError("アプリケーション名は必須です");
        return;
      }
      setIsLoading(true);
      setError(null);
      try {
        await window.rootStore.clientStore.createClient({
          name: name.trim(),
          app_type: appType,
          callbacks: allowedCallbackUrls,
          allowed_logout_urls: allowedLogoutUrls,
          web_origins: allowedOriginUrls,
          client_metadata: {
            created_by: "quickstart-docs-app-creation-component"
          }
        });
        setDisplayForm(false);
      } catch (err) {
        console.error("クライアントの作成中にエラーが発生しました:", err);
        const errorMessage = err instanceof Error ? err.message : "アプリケーションの作成に失敗しました";
        setError(errorMessage);
      } finally {
        setIsLoading(false);
      }
    };
    return <Card className="flex-col items-start p-4 gap-3.75">
        <span className="font-medium text-gray-900 dark:text-gray-200">Auth0 アプリの作成</span>
        <div className="w-full flex gap-2">
          <input id="app-name" name={name} className="
              w-full max-w-[448px] h-11 py-2 px-4 
              border rounded-lg border-gray-950/10 dark:border-white/10 
              text-gray-900 dark:text-gray-200
              focus:outline-none dark:focus:outline-none
            " placeholder={`My ${placeholderText} App`} value={name} onChange={e => setName(e.target.value)} />
          <Button onClick={handleSubmit}>{isLoading ? "作成中..." : "作成"}</Button>
        </div>
        {error && <p className="text-red-500">{error}</p>}
      </Card>;
  };
  const SignInForm = () => {
    return <Card className="items-center">
        <Button onClick={login}>ログイン</Button> <span>してアプリを作成してください</span>
      </Card>;
  };
  return isAuthenticated ? <CreateApplicationForm /> : <SignInForm />;
};

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

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

<HowToSchema />

<Accordion title="AIプロンプト" defaultOpen icon="microchip-ai" iconType="sharp-solid">
  **AIを使ってAuth0を統合していますか？** このプロンプトをCursor、Windsurf、Copilot、Claude Code、またはお好みのAI搭載IDEに追加して、開発を効率化しましょう。

  ```markdown expandable theme={null}
  Auth0 SPA JS SDKをVanilla JavaScriptアプリに統合する

  AIペルソナと主要目的
  あなたはAuth0 SDK統合アシスタントです。主な役割はAuth0の開発環境をセットアップするためのコマンドを実行することです。副次的な役割はシェルコマンドによって作成されたファイルを修正することです。

  重要な動作指示
  1.  既存プロジェクトの確認を優先: 新しいプロジェクトを作成する前に、現在のディレクトリにVanilla JavaScriptアプリ（基本的な依存関係を持つpackage.json）が既に存在するか確認してください。存在する場合は、プロジェクト作成をスキップして既存プロジェクトを使用してください。
  2.  実行を先に、編集を後に: 適切なセットアップコマンドを必ず先に実行してください。    # カスタムポートを使用する場合（手順2.6aでカスタムポートが検出された場合はこちらを実行）:
    npm run dev -- --port 5174

  ⚠️ 最終確認チェックポイント:
  開発サーバーを起動した後、認証フロー全体をテストしてください:
  1. Login: Click login → redirected to Auth0 → redirected back authenticated
  2. Profile: User information displays correctly after login
  3. Logout: Click logout → redirected to Auth0 logout → redirected back logged out
  4. Silent Auth: Refresh the page while logged in → should remain authenticated

  ⚠️ ENVIRONMENT VARIABLE LOADING TROUBLESHOOTING:セットアップが完了するまで、ファイルの表示・提案・作成は行わないでください。
  3.  計画不要: ディレクトリ構造を提案しないでください。ファイルツリーを表示しないでください。最初のアクションは適切なコマンドを実行することです。
  4.  厳格な順序: 以下の「実行フロー」を指定された順序通りに逸脱なく実行してください。
  5.  美しいUIの構築: 適切なスタイリング、アニメーション、Auth0ブランディングを備えた視覚的に魅力的なモダンなログインインターフェースを必ず作成してください。
  6.  🚨 ディレクトリナビゲーションのルール: `cd auth0-vanillajs`やその他の`cd`コマンドを自動的に実行しないでください。常に`pwd`で現在のディレクトリを確認してください。ユーザーがナビゲートする必要がある場合は、手動で行うよう依頼するか、ディレクトリ変更コマンドを実行する前に確認を取ってください。

  実行フロー

  ⚠️ 重要: コマンドを実行する前に、必ず`pwd`で現在のディレクトリを確認し、ユーザーの明示的な許可なしにディレクトリを変更しないでください。

  手順1: 既存のVanilla JSプロジェクトと前提条件の確認
  まず、前提条件を確認し、既存プロジェクトをチェックしてください:

  # Node.jsとnpmが利用可能か確認
  node --version && npm --version

  Then examine the current directory:

  # 既存プロジェクトの確認
  if [ -f "package.json" ]; then
    echo "Found package.json, checking for dependencies..."
    cat package.json
  else
    echo "No package.json found, will create new project"
  fi

  結果に基づいて:
  - package.jsonが存在し基本的な依存関係が含まれている場合は、手順1b（Auth0 SDKのみインストール）に進んでください
  - プロジェクトが存在しない場合は、手順1a（新規プロジェクト作成）に進んでください

  手順1a: 新規プロジェクトの作成とAuth0 SPA JS SDKのインストール
  既存プロジェクトがある場合は、SDKのみインストールしてください:
  npm install @auth0/auth0-spa-js

  ⚠️ CDNの代替手段: ビルドツールを使用しない場合は、npmの代わりにCDN経由でSDKを組み込むことができます:
  <script src="https://cdn.auth0.com/js/auth0-spa-js/2.4/auth0-spa-js.production.js"></script>
  インポートの代わりにグローバルのauth0オブジェクトを使用してください: auth0.createAuth0Client({...})

  それ以外の場合は、新しいプロジェクトを作成してSDKをインストールしてください:

  ⚠️ 重要: プロジェクト作成時に、サブディレクトリではなく現在のディレクトリにプロジェクトファイルが作成される場合があります。このコマンドを実行した後、実際のプロジェクト構造を確認してから次に進んでください。

  MacOS/Linuxの場合:
  mkdir auth0-vanillajs && cd auth0-vanillajs && npm init -y && npm install --save-dev vite && npm install @auth0/auth0-spa-js && touch index.html app.js style.css

  Windows（PowerShell）の場合:
  mkdir auth0-vanillajs; cd auth0-vanillajs; npm init -y; npm install --save-dev vite; npm install @auth0/auth0-spa-js; New-Item -ItemType File -Path index.html, app.js, style.css


  手順2: ファイルの修正と作成
  手順1のコマンドが正常に実行された後、プロジェクトディレクトリ内で以下のファイル操作を行います。

  🚨 ディレクトリナビゲーションのルール:
  1. ユーザーの明示的な確認なしに`cd`コマンドを自動実行しないでください
  2. 作業を進める前に必ず`pwd`で現在のディレクトリを確認してください
  3. 既存プロジェクトで作業する場合: 現在のディレクトリに留まってください
  4. 新規プロジェクトを作成した場合: ユーザーが先にauth0-vanillajsディレクトリに手動で移動する必要があります

  2.1: Auth0環境設定のセットアップ

  ⚠️ 重要: 作業を進める前に現在のディレクトリを確認してください:
  - 新規プロジェクトを作成した場合: auth0-vanillajsディレクトリ内にいる必要があります
  - 既存プロジェクトで作業する場合: プロジェクトのルートディレクトリにいる必要があります
  - `cd auth0-vanillajs`コマンドを実行しないでください。先に正しいディレクトリに移動してください

  手順2.1a: プロジェクトディレクトリへの移動（必要な場合）とAuth0のセットアップ:

  # 新規プロジェクトを作成し、まだauth0-vanillajsにいない場合のみ実行してください:
  cd auth0-vanillajs

  次に、お使いのOSに合わせた環境セットアップコマンドを実行してください:

  ⚠️ 重要なディレクトリ確認手順:
  Auth0 CLIセットアップコマンドを実行する前に、必ず以下を実行してください:

  pwd && ls -la

  これにより、メインディレクトリにいるかサブディレクトリにいるか、またプロジェクトが現在のディレクトリに作成されたか新しいサブディレクトリに作成されたかを確認できます。

  お使いのOSに合わせたAuth0セットアップコマンドを実行してください:

  MacOSの場合:
  # Auth0 CLIがインストールされていない場合はインストール
  brew tap auth0/auth0-cli && brew install auth0

  # Auth0アプリをセットアップして.envファイルを生成
  auth0 qs setup --app --type spa --framework vanilla-javascript --build-tool vite --name "My App" --port 5173

  Windows（PowerShell）の場合:
  # Auth0 CLIがインストールされていない場合はインストール
  scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
  scoop install auth0

  # Auth0アプリをセットアップして.envファイルを生成
  auth0 qs setup --app --type spa --framework vanilla-javascript --build-tool vite --name "My App" --port 5173

  このコマンドは自動的に以下を実行します:
  - Auth0で認証（必要に応じてログインを促します）
  - http://localhost:5173 用に設定されたシングルページアプリケーションを作成
  - VITE_AUTH0_DOMAINとVITE_AUTH0_CLIENT_IDを含む.envファイルを生成


  手順2.1b: 手動の.env.localテンプレートを作成（自動セットアップが失敗した場合）

  cat > .env.local << 'EOF'
  # Auth0設定 - これらの値を更新してください
  VITE_AUTH0_DOMAIN=your-auth0-domain.auth0.com
  VITE_AUTH0_CLIENT_ID=your-auth0-client-id
  EOF

  手順2.1c: 手動セットアップ手順の表示

  echo "📋 MANUAL SETUP REQUIRED:"
  echo "1. Go to https://manage.auth0.com/dashboard/"
  echo "2. Click 'Create Application' → Single Page Application"
  echo "3. Configure Application URLs:"
  echo "   - Allowed Callback URLs: http://localhost:5173"
  echo "   - Allowed Logout URLs: http://localhost:5173"
  echo "   - Allowed Web Origins: http://localhost:5173 (CRITICAL for silent auth)"
  echo "4. Update .env.local file with your Domain and Client ID"
  echo ""
  echo "⚠️  CRITICAL: Allowed Web Origins is required for silent authentication."
  echo "   Without it, users will be logged out when they refresh the page."
  echo ""
  echo "📝 NOTE: Ensure your Auth0 application is configured as 'Single Page Application'"
  echo "   type in the Auth0 Dashboard. Other application types won't work with this SDK."

  2.2: HTML構造の作成
  index.htmlの内容全体を置き換えてください（存在しない場合は作成してください）:

  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Auth0 Vanilla JS</title>
      <link rel="stylesheet" href="style.css" />
    </head>
    <body>
      <div class="app-container">
        <!-- 読み込み中の状態 -->
        <div id="loading" class="loading-state">
          <div class="loading-text">Loading...</div>
        </div>

        <!-- エラー状態 -->
        <div id="error" class="error-state" style="display: none;">
          <div class="error-title">Oops!</div>
          <div class="error-message">Something went wrong</div>
          <div id="error-details" class="error-sub-message"></div>
        </div>

        <!-- メインコンテンツ -->
        <div id="app" class="main-card-wrapper" style="display: none;">
          <img 
            src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png" 
            alt="Auth0 ロゴ" 
            class="auth0-logo"
          />
          <h1 class="main-title">Welcome to Sample0</h1>
          
          <!-- ログアウト状態 -->
          <div id="logged-out" class="action-card">
            <p class="action-text">Get started by signing in to your account</p>
            <button id="login-btn" class="button login">Log In</button>
          </div>

          <!-- ログイン状態 -->
          <div id="logged-in" class="logged-in-section" style="display: none;">
            <div class="logged-in-message">✅ Successfully authenticated!</div>
            <h2 class="profile-section-title">Your Profile</h2>
            <div id="profile" class="profile-card"></div>
            <button id="logout-btn" class="button logout">Log Out</button>
          </div>
        </div>
      </div>

      <script type="module" src="app.js"></script>
    </body>
  </html>

  2.3: アプリケーションロジックの作成
  app.js の内容全体を、適切なエラー処理とAuth0統合を含む以下のコードに置き換えます。

  ⚠️ JavaScriptモジュールのガイドライン:
  - 適切なES6モジュールのインポートを使用してください
  - Auth0初期化に関する包括的なエラー処理を含めてください
  - 環境変数は使用前に検証してください
  - すべての認証状態（読み込み中、エラー、認証済み、未認証）を処理してください

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

  // DOM要素
  const loading = document.getElementById('loading');
  const error = document.getElementById('error');
  const errorDetails = document.getElementById('error-details');
  const app = document.getElementById('app');
  const loggedOutSection = document.getElementById('logged-out');
  const loggedInSection = document.getElementById('logged-in');
  const loginBtn = document.getElementById('login-btn');
  const logoutBtn = document.getElementById('logout-btn');
  const profileContainer = document.getElementById('profile');

  let auth0Client;

  // Auth0クライアントの初期化
  async function initAuth0() {
    try {
      // 環境変数の検証
      const domain = import.meta.env.VITE_AUTH0_DOMAIN;
      const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID;

      if (!domain || !clientId) {
        throw new Error('Auth0 configuration missing. Please check your .env.local file for VITE_AUTH0_DOMAIN and VITE_AUTH0_CLIENT_ID');
      }

      // ドメイン形式の検証
      if (!domain.includes('.auth0.com') && !domain.includes('.us.auth0.com') && !domain.includes('.eu.auth0.com') && !domain.includes('.au.auth0.com')) {
        console.warn('Auth0 domain format might be incorrect. Expected format: your-domain.auth0.com');
      }

      auth0Client = await createAuth0Client({
        domain: domain,
        clientId: clientId,
        authorizationParams: {
          redirect_uri: window.location.origin
        }
      });

      // ログインからのリダイレクト完了を確認
      if (window.location.search.includes('code=') && window.location.search.includes('state=')) {
        await handleRedirectCallback();
      }

      // 認証状態に基づいてUIを更新
      await updateUI();
    } catch (err) {
      console.error('Auth0 initialization error:', err);
      showError(err.message);
    }
  }

  // リダイレクトコールバックの処理
  async function handleRedirectCallback() {
    try {
      await auth0Client.handleRedirectCallback();
      // クエリパラメーターを削除してURLを整理
      window.history.replaceState({}, document.title, window.location.pathname);
    } catch (err) {
      console.error('Redirect callback error:', err);
      showError(err.message);
    }
  }

  // 認証状態に基づいてUIを更新
  async function updateUI() {
    try {
      const isAuthenticated = await auth0Client.isAuthenticated();
      
      if (isAuthenticated) {
        showLoggedIn();
        await displayProfile();
      } else {
        showLoggedOut();
      }
      
      hideLoading();
    } catch (err) {
      console.error('UI update error:', err);
      showError(err.message);
    }
  }

  // ユーザープロフィールの表示
  async function displayProfile() {
    try {
      const user = await auth0Client.getUser();
      const placeholderImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='110' height='110' viewBox='0 0 110 110'%3E%3Ccircle cx='55' cy='55' r='55' fill='%2363b3ed'/%3E%3Cpath d='M55 50c8.28 0 15-6.72 15-15s-6.72-15-15-15-15 6.72-15 15 6.72 15 15 15zm0 7.5c-10 0-30 5.02-30 15v3.75c0 2.07 1.68 3.75 3.75 3.75h52.5c2.07 0 3.75-1.68 3.75-3.75V72.5c0-9.98-20-15-30-15z' fill='%23fff'/%3E%3C/svg%3E`;
      
      profileContainer.innerHTML = `
        <div style="display: flex; flex-direction: column; align-items: center; gap: 1rem;">
          <img 
            src="${user.picture || placeholderImage}" 
            alt="${user.name || 'User'}" 
            class="profile-picture"
            style="
              width: 110px; 
              height: 110px; 
              border-radius: 50%; 
              object-fit: cover;
              border: 3px solid #63b3ed;
            "
            onerror="this.src='${placeholderImage}'"
          />
          <div style="text-align: center;">
            <div class="profile-name" style="font-size: 2rem; font-weight: 600; color: #f7fafc; margin-bottom: 0.5rem;">
              ${user.name || 'User'}
            </div>
            <div class="profile-email" style="font-size: 1.15rem; color: #a0aec0;">
              ${user.email || 'No email provided'}
            </div>
          </div>
        </div>
      `;
    } catch (err) {
      console.error('Error displaying profile:', err);
    }
  }

  // イベントハンドラー
  async function login() {
    try {
      await auth0Client.loginWithRedirect();
    } catch (err) {
      console.error('Login error:', err);
      showError(err.message);
    }
  }

  async function logout() {
    try {
      await auth0Client.logout({
        logoutParams: {
          returnTo: window.location.origin
        }
      });
    } catch (err) {
      console.error('Logout error:', err);
      showError(err.message);
    }
  }

  // UI状態管理
  function showLoading() {
    loading.style.display = 'block';
    error.style.display = 'none';
    app.style.display = 'none';
  }

  function hideLoading() {
    loading.style.display = 'none';
    app.style.display = 'flex';
  }

  function showError(message) {
    loading.style.display = 'none';
    app.style.display = 'none';
    error.style.display = 'block';
    errorDetails.textContent = message;
  }

  function showLoggedIn() {
    loggedOutSection.style.display = 'none';
    loggedInSection.style.display = 'flex';
  }

  function showLoggedOut() {
    loggedInSection.style.display = 'none';
    loggedOutSection.style.display = 'flex';
  }

  // イベントリスナー
  loginBtn.addEventListener('click', login);
  logoutBtn.addEventListener('click', logout);

  // アプリの初期化
  initAuth0();

  ⚠️ チェックポイントの確認:
  JavaScriptロジックを実装したら、以下の基本的な動作を確認してください。
  1. ログインボタンをクリック → Auth0のUniversal Loginページにリダイレクトされる
  2. 認証後 → アプリケーションにリダイレクトされる
  3. リダイレクト後にURLのクエリパラメーターが削除される
  4. Auth0に関連するコンソールエラーが表示されない

  2.4: モダンなCSSスタイリングの追加
  style.css の内容全体を、モダンなAuth0ブランドのスタイリングに置き換えます。

  ⚠️ CSSファイルの置き換え方針:
  既存の style.css ファイルが大きい場合や破損している場合は、まず新しい一時ファイル（例: style-new.css）を作成し、`mv style-new.css style.css` などのターミナルコマンドで元のファイルを置き換えることでファイルの破損を防いでください。

  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }

  body {
    font-family: 'Inter', sans-serif;
    background-color: #1a1e27;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #e2e8f0;
    overflow: hidden;
  }

  .app-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    width: 100%;
    padding: 1rem;
  }

  .loading-state, .error-state {
    background-color: #2d313c;
    border-radius: 15px;
    box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
    padding: 3rem;
    text-align: center;
  }

  .loading-text {
    font-size: 1.8rem;
    font-weight: 500;
    color: #a0aec0;
    animation: pulse 1.5s infinite ease-in-out;
  }

  .error-state {
    background-color: #c53030;
    color: #fff;
  }

  .error-title {
    font-size: 2.8rem;
    font-weight: 700;
    margin-bottom: 0.5rem;
  }

  .error-message {
    font-size: 1.3rem;
    margin-bottom: 0.5rem;
  }

  .error-sub-message {
    font-size: 1rem;
    opacity: 0.8;
  }

  .main-card-wrapper {
    background-color: #262a33;
    border-radius: 20px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2rem;
    padding: 3rem;
    max-width: 500px;
    width: 90%;
    animation: fadeInScale 0.8s ease-out forwards;
  }

  .auth0-logo {
    width: 160px;
    margin-bottom: 1.5rem;
    opacity: 0;
    animation: slideInDown 1s ease-out forwards 0.2s;
  }

  .main-title {
    font-size: 2.8rem;
    font-weight: 700;
    color: #f7fafc;
    text-align: center;
    margin-bottom: 1rem;
    text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
    opacity: 0;
    animation: fadeIn 1s ease-out forwards 0.4s;
  }

  .action-card {
    background-color: #2d313c;
    border-radius: 15px;
    box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
    padding: 2.5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.8rem;
    width: calc(100% - 2rem);
    opacity: 0;
    animation: fadeIn 1s ease-out forwards 0.6s;
  }

  .action-text {
    font-size: 1.25rem;
    color: #cbd5e0;
    text-align: center;
    line-height: 1.6;
    font-weight: 400;
  }

  .button {
    padding: 1.1rem 2.8rem;
    font-size: 1.2rem;
    font-weight: 600;
    border-radius: 10px;
    border: none;
    cursor: pointer;
    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    outline: none;
  }

  .button:focus {
    box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
  }

  .button.login {
    background-color: #63b3ed;
    color: #1a1e27;
  }

  .button.login:hover {
    background-color: #4299e1;
    transform: translateY(-5px) scale(1.03);
    box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
  }

  .button.logout {
    background-color: #fc8181;
    color: #1a1e27;
  }

  .button.logout:hover {
    background-color: #e53e3e;
    transform: translateY(-5px) scale(1.03);
    box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
  }

  .logged-in-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.5rem;
    width: 100%;
  }

  .logged-in-message {
    font-size: 1.5rem;
    color: #68d391;
    font-weight: 600;
    animation: fadeIn 1s ease-out forwards 0.8s;
  }

  .profile-section-title {
    font-size: 2.2rem;
    animation: slideInUp 1s ease-out forwards 1s;
  }

  .profile-card {
    padding: 2.2rem;
    animation: scaleIn 0.8s ease-out forwards 1.2s;
  }

  .profile-picture {
    transition: transform 0.3s ease-in-out;
  }

  .profile-picture:hover {
    transform: scale(1.05);
  }

  /* アニメーション */
  @keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
  }

  @keyframes fadeInScale {
    from { opacity: 0; transform: scale(0.95); }
    to { opacity: 1; transform: scale(1); }
  }

  @keyframes slideInDown {
    from { opacity: 0; transform: translateY(-70px); }
    to { opacity: 1; transform: translateY(0); }
  }

  @keyframes slideInUp {
    from { opacity: 0; transform: translateY(50px); }
    to { opacity: 1; transform: translateY(0); }
  }

  @keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.6; }
  }

  @keyframes scaleIn {
    from { opacity: 0; transform: scale(0.8); }
    to { opacity: 1; transform: scale(1); }
  }

  /* レスポンシブデザイン */
  @media (max-width: 600px) {
    .main-card-wrapper {
      padding: 2rem;
      margin: 1rem;
    }
    
    .main-title {
      font-size: 2.2rem;
    }
    
    .button {
      padding: 1rem 2rem;
      font-size: 1.1rem;
    }
    
    .auth0-logo {
      width: 120px;
    }

    .action-card {
      padding: 2rem;
    }
  }

  2.5: Update package.json with development script
  Update your package.json to include development, build, and preview scripts. Replace the entire contents of package.json:

  cat > package.json << 'EOF'
  {
    "name": "auth0-vanillajs",
    "version": "1.0.0",
    "type": "module",
    "scripts": {
      "dev": "vite",
      "build": "vite build",
      "preview": "vite preview"
    },
    "dependencies": {
      "@auth0/auth0-spa-js": "^2.4.1"
    },
    "devDependencies": {
      "vite": "^5.0.0"
    }
  }
  EOF

  2.6: Start the development server

  ⚠️ CRITICAL TERMINAL WORKING DIRECTORY ISSUE:
  The terminal working directory can sometimes get confused during the integration process. Before starting the dev server, ALWAYS:

  1. Verify you're in the correct directory with explicit path change:

  cd /full/absolute/path/to/project && pwd

  2. Check for package.json in current directory:

  ls -la package.json

  3. If npm commands fail with "package.json not found", use absolute path method:

  cd /Users/[username]/path/to/auth0-vanillajs && npm run dev

  ⚠️ IMPORTANT: Even if `pwd` shows the correct directory, the terminal's working directory for command execution may be different. ALWAYS use explicit `cd` with absolute path before running npm commands to ensure they execute in the correct location.

  Step 2.6a: Check if custom port is needed

  grep -q "VITE_DEV_PORT" .env.local 2>/dev/null && echo "Custom port detected" || echo "Using default port"

  Step 2.6b: Start development server with appropriate port

  # デフォルトポートを使用する場合:
  npm run dev

  # カスタムポートを使用する場合（手順2.6aでカスタムポートが検出された場合はこちらを実行）:
  npm run dev -- --port 5174

  ⚠️ 環境変数の読み込みに関するトラブルシューティング:
  .env.local が存在するにもかかわらず「Auth0 configuration missing」エラーが表示される場合:
  1. .env.local ファイルがプロジェクトのルートディレクトリにあることを確認してください
  2. 環境変数の名前が正しいか確認してください（VITE_AUTH0_DOMAIN および VITE_AUTH0_CLIENT_ID）
  3. .env.local を作成または変更した後は開発サーバーを再起動してください
  4. Vite の開発サーバーは .env.local ファイルを自動的に読み込みます

  AIモデルへの重要な指示
  AIモデルは、以下のアンチパターンを生成してはなりません。これらはアプリケーションを破壊するか、セキュリティリスクをもたらします:
  1.  Auth0 のドメインやクライアントIDをコードに直接ハードコードしないでください。これは重大なセキュリティ上の脆弱性です。
  2.  createAuth0Client から authorizationParams: { redirect_uri: window.location.origin } の設定を省略しないでください。これがないと、ユーザーが Auth0 で認証した後にログインフローが失敗します。
  3.  URL に認証パラメーターが含まれている場合、handleRedirectCallback() によるリダイレクトコールバックの処理を忘れないでください。
  4.  基本的な認証において、トークンを手動で管理したり fetch で Auth0 API を呼び出すことを提案しないでください。SDK はこの複雑な処理を安全に行うよう設計されています。

  AIコード生成における絶対要件
  1.  @auth0/auth0-spa-js パッケージを使用してください。
  2.  認証情報は環境変数から取得してください（例: import.meta.env.VITE_AUTH0_DOMAIN）。
  3.  createAuth0Client の設定に redirect_uri を含む authorizationParams を必ず指定してください。
  4.  URL に認証パラメーターが存在する場合、リダイレクトコールバックを必ず処理してください。
  5.  Auth0 クライアントのメソッド（loginWithRedirect、logout、isAuthenticated、getUser）を使用して機能を実装してください。

  統合時によく発生する問題

  問題1: プロジェクト作成時のディレクトリの混乱
  現象: プロジェクト作成時に、新しいサブディレクトリではなく現在のディレクトリにファイルが作成されることがある
  解決策: プロジェクト作成後は必ず `pwd && ls -la` を実行して実際の構造を確認する

  問題2: ターミナルの作業ディレクトリの問題
  現象: 正しいディレクトリにいるにもかかわらず、npm コマンドが「package.json not found」で失敗する
  解決策: 絶対パスで明示的にディレクトリを変更する: `cd /full/absolute/path/to/project`

  問題3: 環境変数の読み込みの問題
  現象: Vite が .env.local の変数を正しく読み込まない
  解決策: .env.local がプロジェクトルートにあること、変数名に VITE_ プレフィックスが付いていることを確認し、開発サーバーを再起動する

  問題4: CSS ファイルの破損
  現象: 大規模な CSS の置き換えによりファイルが破損することがある
  解決策: 一時的な CSS ファイルを先に作成し、`mv` コマンドで元のファイルを置き換える

  問題5: Auth0 設定の検証
  現象: 無効なドメイン形式や設定の欠落により初期化が失敗する
  解決策: クライアント作成前に Auth0 設定の適切なバリデーションとエラーメッセージを追加する

  問題6: ターミナルの作業ディレクトリがプロジェクトルートにない
  現象: pwd では正しいパスが表示されているにもかかわらず、ターミナルが auth0-vanillajs ディレクトリにないため、AIエージェントが `npm run dev` の実行に失敗する
  解決策: npm コマンドを実行する前に、必ず絶対パスで明示的にディレクトリを変更する:

  cd auth0-vanillajs && npm run dev

  ターミナルの作業ディレクトリは表示されているパスと乖離することがあるため、npm コマンドが正しい場所で実行されるよう明示的にディレクトリを指定する必要があります。

  高度な機能の実装

  ⚠️ SDK 機能に関する注意:
  SDK の isAuthenticated() 関数を使用すると、ログイン/ログアウトボタンやユーザーコンテンツの条件付きレンダリングが可能になります。上記の実装では、認証状態に基づいて異なる UI セクションを表示することでこのパターンを示しています。

  API 呼び出し用のアクセストークン:
  保護された API を呼び出す必要がある場合は、アクセストークンを取得できます:

  // この関数を app.js に追加してください
  async function getAccessToken() {
    try {
      const token = await auth0Client.getTokenSilently({
        authorizationParams: {
          audience: 'YOUR_API_IDENTIFIER',
          scope: 'read:messages'
        }
      });
      
      // トークンを使用して API を呼び出す
      const response = await fetch('/api/protected', {
        headers: {
          Authorization: `Bearer ${token}`
        }
      });
      
      const data = await response.json();
      console.log(data);
    } catch (error) {
      console.error('Error getting token:', error);
    }
  }

  ポップアップベースのログイン:
  よりスムーズなユーザー体験のために、ポップアップベースのログインを使用できます:

  // app.js のログイン関数を置き換えてください
  async function login() {
    try {
      await auth0Client.loginWithPopup();
      await updateUI();
    } catch (err) {
      if (err.error !== 'popup_closed_by_user') {
        showError(err.message);
      }
    }
  }

  組織のサポート:
  Auth0 の組織機能を使用している場合:

  // Auth0 クライアントの設定を更新してください
  auth0Client = await createAuth0Client({
    domain: import.meta.env.VITE_AUTH0_DOMAIN,
    clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
    authorizationParams: {
      redirect_uri: window.location.origin,
      organization: 'YOUR_ORGANIZATION_ID' // またはユーザーに選択を促す
    }
  });
  ```
</Accordion>

<Note>
  **前提条件:** 始める前に、以下がインストールされていることを確認してください。

  * **[Node.js](https://nodejs.org/en/download)** 20 LTS 以降
  * **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 10 以降、または **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22 以降、または **[pnpm](https://pnpm.io/installation)** 8 以降

  インストールを確認するには、次を実行します: `node --version && npm --version`

  **ビルドツール:** このクイックスタートでは、開発に **Vite** を使用します。ビルドツールを使用しない構成の場合は、CDN 経由で SDK を使用することもできます。
</Note>

<div id="get-started">
  ## はじめに
</div>

このクイックスタートでは、vanilla JavaScript アプリケーションに Auth0 認証を追加する方法を説明します。プレーンな JavaScript と Auth0 SPA SDK を使用して、安全なログイン機能を備えたモダンなシングルページアプリケーションを作成します。

export const localEnvSnippet = `VITE_AUTH0_DOMAIN={yourDomain}
VITE_AUTH0_CLIENT_ID={yourClientId}`;

<Steps>
  <Step title="新規プロジェクトを作成" stepNumber={1}>
    このクイックスタート用に新しい JavaScript プロジェクトを作成します

    ```shellscript theme={null}
    mkdir auth0-vanillajs && cd auth0-vanillajs
    ```

    プロジェクトを初期化し、ローカルの開発サーバーをインストールして、スクリプトを設定する

    ```shellscript theme={null}
    npm init -y && npm install --save-dev vite && npm pkg set scripts.dev="vite" scripts.build="vite build" scripts.preview="vite preview" type="module"
    ```

    基本的なプロジェクト構成を作成します

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      touch index.html app.js style.css
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType File -Path index.html, app.js, style.css
      ```
    </CodeGroup>
  </Step>

  <Step title="Auth0 SPA JS SDKをインストールする" stepNumber={2}>
    ```shellscript theme={null}
    npm install @auth0/auth0-spa-js
    ```
  </Step>

  <Step title="Auth0アプリを設定する" stepNumber={3}>
    次に、Auth0テナントに新しいアプリを作成し、プロジェクトに環境変数を追加します。

    Auth0アプリの設定方法は 3 つあります。Quick Setup ツール (推奨) を使う方法、CLI コマンドを実行する方法、または Dashboard から手動で設定する方法です。

    <Tabs>
      <Tab title="Quick Setup（推奨）">
        Auth0アプリを作成し、適切な設定値があらかじめ入力された `.env` ファイルをコピーします。

        <CreateInteractiveApp placeholderText="Vanilla JS" appType="spa" allowedCallbackUrls={["http://localhost:5173"]} allowedLogoutUrls={["http://localhost:5173"]} allowedOriginUrls={["http://localhost:5173"]} />

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env.local" />
      </Tab>

      <Tab title="CLI">
        プロジェクトのルートディレクトリで次のコマンドを実行し、Auth0アプリを作成して `.env` ファイルを生成します。

        <CodeGroup>
          ```shellscript Mac theme={null}
          # Auth0 CLI をインストールします（未インストールの場合）
          brew tap auth0/auth0-cli && brew install auth0

          # Auth0 アプリを設定し、.env ファイルを生成します
          auth0 qs setup --app --type spa --framework vanilla-javascript --build-tool vite --name "My App" --port 5173
          ```

          ```powershell Windows theme={null}
          # Auth0 CLI をインストールします（未インストールの場合）
          scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
          scoop install auth0

          # Auth0 アプリを設定し、.env ファイルを生成します
          auth0 qs setup --app --type spa --framework vanilla-javascript --build-tool vite --name "My App" --port 5173
          ```
        </CodeGroup>

        <Note>
          このコマンドでは次の処理が行われます。

          1. 認証済みかどうかを確認します (必要に応じてログインを求めます)
          2. `http://localhost:5173` 用に設定された Auth0 Single Page Application を作成します
          3. `VITE_AUTH0_DOMAIN` と `VITE_AUTH0_CLIENT_ID` を含む `.env` ファイルを生成します
        </Note>
      </Tab>

      <Tab title="Dashboard">
        開始する前に、プロジェクトのルートディレクトリに `.env.local` ファイルを作成します

        ```shellscript .env.local theme={null}
        VITE_AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
        VITE_AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
        ```

        1. [Auth0 Dashboard](https://manage.auth0.com/dashboard/) に移動します
        2. **Applications** > **Applications** > **Create Application** をクリックします
        3. ポップアップでアプリの名前を入力し、アプリの種類として `Single Page Web Application` を選択して **Create** をクリックします
        4. Application Details ページで **Settings** タブに切り替えます
        5. `.env.local` ファイル内の `YOUR_AUTH0_APP_DOMAIN` と `YOUR_AUTH0_APP_CLIENT_ID` を、Dashboard の **Domain** と **Client ID** の値に置き換えます

        最後に、Application Details ページの **Settings** タブで次の URL を設定します。

        **Allowed Callback URLs:**

        ```
        http://localhost:5173
        ```

        **Allowed Logout URLs:**

        ```
        http://localhost:5173
        ```

        **Allowed Web Origins:**

        ```
        http://localhost:5173
        ```

        <Info>
          **Allowed Callback URLs** は、認証後にユーザーを安全にアプリケーションへ戻すための重要なセキュリティ対策です。一致する URL がない場合、ログイン処理は失敗し、ユーザーはアプリにアクセスできず、代わりに Auth0 のエラーページが表示されます。

          **Allowed Logout URLs** は、サインアウト時にシームレスなユーザー体験を提供するうえで重要です。一致する URL がない場合、ユーザーはログアウト後にアプリケーションへリダイレクトされず、代わりに汎用的な Auth0 ページに移動します。

          **Allowed Web Origins** は、サイレント認証に不可欠です。これがないと、ユーザーはページを更新したときや後でアプリに戻ったときにログアウトされます。
        </Info>
      </Tab>
    </Tabs>
  </Step>

  <Step title="HTML の構造とアプリケーションロジックを作成する" stepNumber={4}>
    アプリケーションファイルを作成します：

    <AuthCodeGroup>
      ```html index.html expandable lines theme={null}
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>Auth0 Vanilla JS</title>
          <link rel="stylesheet" href="style.css" />
        </head>
        <body>
          <div class="app-container">
            <!-- 読み込み中の状態 -->
            <div id="loading" class="loading-state">
              <div class="loading-text">Loading...</div>
            </div>

            <!-- エラー状態 -->
            <div id="error" class="error-state" style="display: none;">
              <div class="error-title">Oops!</div>
              <div class="error-message">Something went wrong</div>
              <div id="error-details" class="error-sub-message"></div>
            </div>

            <!-- メインコンテンツ -->
            <div id="app" class="main-card-wrapper" style="display: none;">
              <img 
                src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png" 
                alt="Auth0 Logo" 
                class="auth0-logo"
              />
              <h1 class="main-title">Welcome to Sample0</h1>
              
              <!-- ログアウト状態 -->
              <div id="logged-out" class="action-card">
                <p class="action-text">Get started by signing in to your account</p>
                <button id="login-btn" class="button login">Log In</button>
              </div>

              <!-- ログイン状態 -->
              <div id="logged-in" class="logged-in-section" style="display: none;">
                <div class="logged-in-message">✅ Successfully authenticated!</div>
                <h2 class="profile-section-title">Your Profile</h2>
                <div id="profile" class="profile-card"></div>
                <button id="logout-btn" class="button logout">Log Out</button>
              </div>
            </div>
          </div>

          <script type="module" src="app.js"></script>
        </body>
      </html>
      ```

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

      // DOM要素
      const loading = document.getElementById('loading');
      const error = document.getElementById('error');
      const errorDetails = document.getElementById('error-details');
      const app = document.getElementById('app');
      const loggedOutSection = document.getElementById('logged-out');
      const loggedInSection = document.getElementById('logged-in');
      const loginBtn = document.getElementById('login-btn');
      const logoutBtn = document.getElementById('logout-btn');
      const profileContainer = document.getElementById('profile');

      let auth0Client;

      // Auth0クライアントの初期化
      async function initAuth0() {
        try {
          auth0Client = await createAuth0Client({
            domain: import.meta.env.VITE_AUTH0_DOMAIN,
            clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
            authorizationParams: {
              redirect_uri: window.location.origin
            }
          });

          // ログイン後のリダイレクトかどうかを確認
          if (window.location.search.includes('code=') && window.location.search.includes('state=')) {
            await handleRedirectCallback();
          }

          // 認証状態に基づいてUIを更新
          await updateUI();
        } catch (err) {
          showError(err.message);
        }
      }

      // リダイレクトコールバックの処理
      async function handleRedirectCallback() {
        try {
          await auth0Client.handleRedirectCallback();
          // クエリパラメーターを削除してURLを整理
          window.history.replaceState({}, document.title, window.location.pathname);
        } catch (err) {
          showError(err.message);
        }
      }

      // 認証状態に基づいてUIを更新
      async function updateUI() {
        try {
          const isAuthenticated = await auth0Client.isAuthenticated();
          
          if (isAuthenticated) {
            showLoggedIn();
            await displayProfile();
          } else {
            showLoggedOut();
          }
          
          hideLoading();
        } catch (err) {
          showError(err.message);
        }
      }

      // ユーザープロフィールの表示
      async function displayProfile() {
        try {
          const user = await auth0Client.getUser();
          const placeholderImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='110' height='110' viewBox='0 0 110 110'%3E%3Ccircle cx='55' cy='55' r='55' fill='%2363b3ed'/%3E%3Cpath d='M55 50c8.28 0 15-6.72 15-15s-6.72-15-15-15-15 6.72-15 15 6.72 15 15 15zm0 7.5c-10 0-30 5.02-30 15v3.75c0 2.07 1.68 3.75 3.75 3.75h52.5c2.07 0 3.75-1.68 3.75-3.75V72.5c0-9.98-20-15-30-15z' fill='%23fff'/%3E%3C/svg%3E`;
          
          profileContainer.innerHTML = `
            <div style="display: flex; flex-direction: column; align-items: center; gap: 1rem;">
              <img 
                src="${user.picture || placeholderImage}" 
                alt="${user.name || 'User'}" 
                class="profile-picture"
                style="
                  width: 110px; 
                  height: 110px; 
                  border-radius: 50%; 
                  object-fit: cover;
                  border: 3px solid #63b3ed;
                "
                onerror="this.src='${placeholderImage}'"
              />
              <div style="text-align: center;">
                <div class="profile-name" style="font-size: 2rem; font-weight: 600; color: #f7fafc; margin-bottom: 0.5rem;">
                  ${user.name || 'User'}
                </div>
                <div class="profile-email" style="font-size: 1.15rem; color: #a0aec0;">
                  ${user.email || 'No email provided'}
                </div>
              </div>
            </div>
          `;
        } catch (err) {
          console.error('Error displaying profile:', err);
        }
      }

      // イベントハンドラー
      async function login() {
        try {
          await auth0Client.loginWithRedirect();
        } catch (err) {
          showError(err.message);
        }
      }

      async function logout() {
        try {
          await auth0Client.logout({
            logoutParams: {
              returnTo: window.location.origin
            }
          });
        } catch (err) {
          showError(err.message);
        }
      }

      // UI状態管理
      function showLoading() {
        loading.style.display = 'block';
        error.style.display = 'none';
        app.style.display = 'none';
      }

      function hideLoading() {
        loading.style.display = 'none';
        app.style.display = 'flex';
      }

      function showError(message) {
        loading.style.display = 'none';
        app.style.display = 'none';
        error.style.display = 'block';
        errorDetails.textContent = message;
      }

      function showLoggedIn() {
        loggedOutSection.style.display = 'none';
        loggedInSection.style.display = 'flex';
      }

      function showLoggedOut() {
        loggedInSection.style.display = 'none';
        loggedOutSection.style.display = 'flex';
      }

      // イベントリスナー
      loginBtn.addEventListener('click', login);
      logoutBtn.addEventListener('click', logout);

      // アプリの初期化
      initAuth0();
      ```

      ```css style.css expandable lines theme={null}
      @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        font-family: 'Inter', sans-serif;
        background-color: #1a1e27;
        min-height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #e2e8f0;
        overflow: hidden;
      }

      .app-container {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        min-height: 100vh;
        width: 100%;
        padding: 1rem;
      }

      .loading-state, .error-state {
        background-color: #2d313c;
        border-radius: 15px;
        box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
        padding: 3rem;
        text-align: center;
      }

      .loading-text {
        font-size: 1.8rem;
        font-weight: 500;
        color: #a0aec0;
        animation: pulse 1.5s infinite ease-in-out;
      }

      .error-state {
        background-color: #c53030;
        color: #fff;
      }

      .error-title {
        font-size: 2.8rem;
        font-weight: 700;
        margin-bottom: 0.5rem;
      }

      .error-message {
        font-size: 1.3rem;
        margin-bottom: 0.5rem;
      }

      .error-sub-message {
        font-size: 1rem;
        opacity: 0.8;
      }

      .main-card-wrapper {
        background-color: #262a33;
        border-radius: 20px;
        box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 2rem;
        padding: 3rem;
        max-width: 500px;
        width: 90%;
        animation: fadeInScale 0.8s ease-out forwards;
      }

      .auth0-logo {
        width: 160px;
        margin-bottom: 1.5rem;
        opacity: 0;
        animation: slideInDown 1s ease-out forwards 0.2s;
      }

      .main-title {
        font-size: 2.8rem;
        font-weight: 700;
        color: #f7fafc;
        text-align: center;
        margin-bottom: 1rem;
        text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
        opacity: 0;
        animation: fadeIn 1s ease-out forwards 0.4s;
      }

      .action-card {
        background-color: #2d313c;
        border-radius: 15px;
        box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
        padding: 2.5rem;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 1.8rem;
        width: calc(100% - 2rem);
        opacity: 0;
        animation: fadeIn 1s ease-out forwards 0.6s;
      }

      .action-text {
        font-size: 1.25rem;
        color: #cbd5e0;
        text-align: center;
        line-height: 1.6;
        font-weight: 400;
      }

      .button {
        padding: 1.1rem 2.8rem;
        font-size: 1.2rem;
        font-weight: 600;
        border-radius: 10px;
        border: none;
        cursor: pointer;
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
        box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
        text-transform: uppercase;
        letter-spacing: 0.08em;
        outline: none;
      }

      .button:focus {
        box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
      }

      .button.login {
        background-color: #63b3ed;
        color: #1a1e27;
      }

      .button.login:hover {
        background-color: #4299e1;
        transform: translateY(-5px) scale(1.03);
        box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
      }

      .button.logout {
        background-color: #fc8181;
        color: #1a1e27;
      }

      .button.logout:hover {
        background-color: #e53e3e;
        transform: translateY(-5px) scale(1.03);
        box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
      }

      .logged-in-section {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 1.5rem;
        width: 100%;
      }

      .logged-in-message {
        font-size: 1.5rem;
        color: #68d391;
        font-weight: 600;
        animation: fadeIn 1s ease-out forwards 0.8s;
      }

      .profile-section-title {
        font-size: 2.2rem;
        animation: slideInUp 1s ease-out forwards 1s;
      }

      .profile-card {
        padding: 2.2rem;
        animation: scaleIn 0.8s ease-out forwards 1.2s;
      }

      .profile-picture {
        transition: transform 0.3s ease-in-out;
      }

      .profile-picture:hover {
        transform: scale(1.05);
      }

      /* アニメーション */
      @keyframes fadeIn {
        from { opacity: 0; }
        to { opacity: 1; }
      }

      @keyframes fadeInScale {
        from { opacity: 0; transform: scale(0.95); }
        to { opacity: 1; transform: scale(1); }
      }

      @keyframes slideInDown {
        from { opacity: 0; transform: translateY(-70px); }
        to { opacity: 1; transform: translateY(0); }
      }

      @keyframes slideInUp {
        from { opacity: 0; transform: translateY(50px); }
        to { opacity: 1; transform: translateY(0); }
      }

      @keyframes pulse {
        0%, 100% { opacity: 1; }
        50% { opacity: 0.6; }
      }

      @keyframes scaleIn {
        from { opacity: 0; transform: scale(0.8); }
        to { opacity: 1; transform: scale(1); }
      }

      /* レスポンシブデザイン */
      @media (max-width: 600px) {
        .main-card-wrapper {
          padding: 2rem;
          margin: 1rem;
        }
        
        .main-title {
          font-size: 2.2rem;
        }
        
        .button {
          padding: 1rem 2rem;
          font-size: 1.1rem;
        }
        
        .auth0-logo {
          width: 120px;
        }

        .action-card {
          padding: 2rem;
        }
      }
      ```
    </AuthCodeGroup>
  </Step>

  <Step title="アプリを実行する" stepNumber={5}>
    ```shellscript theme={null}
    npm run dev
    ```

    <Info>
      ポート 5173 が使用中の場合は、`npm run dev -- --port 5174` を実行し、Auth0 アプリのコールバック URL を `http://localhost:5174` に更新してください
    </Info>
  </Step>
</Steps>

<Check>
  **チェックポイント**

  これで、[localhost](http://localhost:5173/) で完全に動作する Auth0 のログインページが実行されているはずです
</Check>

***

<div id="advanced-usage">
  ## 高度な使い方
</div>

<Accordion title="API 呼び出し用のアクセストークンを取得">
  保護された API を呼び出す必要がある場合は、アクセストークンを取得できます。

  ```javascript theme={null}
  // これを app.js に追加します
  async function getAccessToken() {
    try {
      const token = await auth0Client.getTokenSilently({
        authorizationParams: {
          audience: 'YOUR_API_IDENTIFIER',
          scope: 'read:messages'
        }
      });
      
      // トークンを使用して API を呼び出します
      const response = await fetch('/api/protected', {
        headers: {
          Authorization: `Bearer ${token}`
        }
      });
      
      const data = await response.json();
      console.log(data);
    } catch (error) {
      console.error('Error getting token:', error);
    }
  }
  ```
</Accordion>

<Accordion title="ポップアップでログインを処理">
  よりスムーズなユーザー体験のために、ポップアップベースのログインを使用できます。

  ```javascript theme={null}
  // app.js の login 関数を置き換えます
  async function login() {
    try {
      await auth0Client.loginWithPopup();
      await updateUI();
    } catch (err) {
      if (err.error !== 'popup_closed_by_user') {
        showError(err.message);
      }
    }
  }
  ```
</Accordion>

<Accordion title="組織のサポートを追加">
  Auth0 Organizations を使用している場合:

  ```javascript theme={null}
  // Auth0 クライアントの設定を更新します
  auth0Client = await createAuth0Client({
    domain: import.meta.env.VITE_AUTH0_DOMAIN,
    clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
    authorizationParams: {
      redirect_uri: window.location.origin,
      organization: 'YOUR_ORGANIZATION_ID' // または、ユーザーに選択させます
    }
  });
  ```
</Accordion>
