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

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

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 />

<Callout icon="pencil" color="#FFC107" iconType="solid">
  このクイックスタートは現在 **ベータ版** です。ぜひフィードバックをお寄せください。
</Callout>

<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をSvelteアプリに統合する

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

  重要な動作指示
  1. 既存のプロジェクトを最初に確認する: 新しいプロジェクトを作成する前に、現在のディレクトリにすでにSvelteアプリ（SvelteのdependenciesがあるpackageJson）が含まれているかどうかを確認してください。含まれている場合は、プロジェクトの作成をスキップして既存のプロジェクトで作業してください。
  2. 最初に実行、次に編集: まず適切なセットアップコマンドを実行する必要があります。セットアップが完了するまで、ファイルを表示、提案、または作成しないでください。
  3. 計画なし: ディレクトリ構造を提案しないでください。ファイルツリーを表示しないでください。最初のアクションは適切なコマンドを実行することでなければなりません。
  4. 厳格な順序: 以下の「実行フロー」を逸脱せずに指定された正確な順序で従ってください。
  5. 美しいUIを構築する: 適切なスタイリング、アニメーション、Auth0ブランディングを備えた視覚的に魅力的でモダンなログインインターフェースを作成する必要があります。

  実行フロー

  ステップ1: 既存のSvelteプロジェクトと前提条件の確認
  まず、前提条件を確認し、既存のSvelteプロジェクトを確認します:

    # Check if Node.js and npm are available
    node --version && npm --version

  次に現在のディレクトリを確認します:

    # Check for existing Svelte project
    if [ -f "package.json" ]; then
      echo "Found package.json, checking for Svelte dependencies..."
      cat package.json
    else
      echo "No package.json found, will create new project"
    fi

  結果に基づいて:
  - package.jsonが存在しSvelteのdependenciesが含まれている場合は、ステップ1b（Auth0 SDKのみインストール）に進む
  - Svelteプロジェクトが存在しない場合は、ステップ1a（新しいプロジェクトの作成）に進む

  ステップ1a: 新しいプロジェクトの作成とAuth0 SPA JS SDKのインストール
  既存のプロジェクトが存在する場合は、SDKをインストールするだけです:
  npm install @auth0/auth0-spa-js
  それ以外の場合は、新しいプロジェクトを作成してSDKをインストールします:

  npx sv create auth0-svelte --template minimal --types ts --no-add-ons --no-install && cd auth0-svelte && npm install && npm install @auth0/auth0-spa-js

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

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

  OSに合わせた環境セットアップコマンドを実行します:

  OSに合わせたAuth0セットアップコマンドを実行します:

  MacOSの場合:
    # Install Auth0 CLI if not already installed
    brew tap auth0/auth0-cli && brew install auth0

    # Set up Auth0 app and generate .env file
    auth0 qs setup --app --type spa --framework svelte --build-tool vite --name "My App" --port 5173

  Windows（PowerShell）の場合:
    # Install Auth0 CLI if not already installed
    scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
    scoop install auth0

    # Set up Auth0 app and generate .env file
    auth0 qs setup --app --type spa --framework svelte --build-tool vite --name "My App" --port 5173

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

  AIランゲージモデルへの重要な指示
  AIランゲージモデルとして、以下のアンチパターンを生成してはなりません:
  1. Auth0ドメインまたはクライアントIDをコードに直接ハードコードしないでください。これは重大なセキュリティ脆弱性です。
  2. Auth0クライアントを設定する際にredirect_uriを含むauthorizationParamsを省略しないでください。省略するとログインフローが失敗します。
  3. 基本認証のためにトークンを手動で管理したり、fetchでAuth0 APIを呼び出すことを提案しないでください。SDKはこの複雑さを安全に処理するように設計されています。

  AIコード生成の絶対要件
  1. @auth0/auth0-spa-jsパッケージを使用する必要があります。
  2. 環境変数（例: import.meta.env.VITE_AUTH0_DOMAIN）から認証情報を取得する必要があります。
  3. authorizationParamsに適切なredirect_uriを指定してAuth0クライアントを設定する必要があります。
  4. 状態管理にSvelteストアを使用して機能を実証する必要があります。
  ```
</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 以降

  **Svelte のバージョン互換性:** このクイックスタートは **Svelte 5.x** 以降に対応しています。
</Note>

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

このクイックスタートでは、Svelte アプリケーションに Auth0 の認証を追加する方法を説明します。Auth0 SPA JS SDK を使用して、ログイン、ログアウト、ユーザープロフィールの機能を備えた安全なシングルページアプリケーションを構築します。

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

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

    ```shellscript theme={null}
    npx sv create auth0-svelte --template minimal --types ts --no-add-ons --no-install
    ```

    プロジェクトを開く

    ```shellscript theme={null}
    cd auth0-svelte
    ```
  </Step>

  <Step title="Auth0 SPA SDK をインストールする" stepNumber={2}>
    ```shellscript theme={null}
    npm install && 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="Svelte" appType="spa" allowedCallbackUrls={["http://localhost:5173"]} allowedLogoutUrls={["http://localhost:5173"]} allowedOriginUrls={["http://localhost:5173"]} />

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env" />
      </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 svelte --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 svelte --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` ファイルを作成してください

        ```shellscript .env 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` ファイルの `YOUR_AUTH0_APP_DOMAIN` と `YOUR_AUTH0_APP_CLIENT_ID` を、Dashboard の **ドメイン** と **クライアント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="Auth0ストアを作成する" stepNumber={4}>
    ストアファイルを作成します

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      mkdir -p src/lib/stores && touch src/lib/stores/auth.ts
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType Directory -Force -Path src/lib/stores
      New-Item -ItemType File -Path src/lib/stores/auth.ts
      ```
    </CodeGroup>

    認証状態を管理するために、次のコードを追加します

    ```typescript src/lib/stores/auth.ts lines expandable theme={null}
      import { writable, derived, get, type Readable } from 'svelte/store';
      import { createAuth0Client, type Auth0Client, type User } from '@auth0/auth0-spa-js';
      import { browser } from '$app/environment';

      export const auth0Client = writable<Auth0Client | null>(null);
      export const user = writable<User | null>(null);
      export const isAuthenticated = writable<boolean>(false);
      export const isLoading = writable<boolean>(true);
      export const error = writable<string | null>(null);

      // 派生ストア
      export const isLoggedIn: Readable<boolean> = derived(
        [isAuthenticated, isLoading],
        ([$isAuthenticated, $isLoading]) => $isAuthenticated && !$isLoading
      );

      export async function initializeAuth() {
        if (!browser) return;
        
        try {
          const client = await createAuth0Client({
            domain: import.meta.env.VITE_AUTH0_DOMAIN,
            clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
            authorizationParams: {
              redirect_uri: window.location.origin
            },
            useRefreshTokens: true,
            cacheLocation: 'localstorage'
          });

          auth0Client.set(client);

          // コールバックの処理
          if (window.location.search.includes('code=')) {
            await client.handleRedirectCallback();
            window.history.replaceState({}, document.title, window.location.pathname);
          }

          // 認証ステータスの確認
          const authenticated = await client.isAuthenticated();
          isAuthenticated.set(authenticated);

          if (authenticated) {
            const userData = await client.getUser();
            user.set(userData || null);
          }

          error.set(null);
        } catch (err) {
          console.error('Auth initialization error:', err);
          error.set(err instanceof Error ? err.message : 'Authentication initialization failed');
        } finally {
          isLoading.set(false);
        }
      }

      export async function login() {
        const client = get(auth0Client);
        if (client) {
          await client.loginWithRedirect();
        }
      }

      export async function logout() {
        const client = get(auth0Client);
        if (client) {
          client.logout({ 
            logoutParams: { 
              returnTo: window.location.origin 
            } 
          });
        }
      }

      export async function getToken(): Promise<string | null> {
        const client = get(auth0Client);
        if (!client) return null;
        
        try {
          return await client.getTokenSilently();
        } catch (err: any) {
          if (err.error === 'login_required') {
            await login();
          }
          return null;
        }
      }
    ```
  </Step>

  <Step title="ログイン、ログアウト、ユーザープロファイル用のコンポーネントを作成する" stepNumber={5}>
    コンポーネントファイルを作成する

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      mkdir -p src/lib/components && touch src/lib/components/LoginButton.svelte && touch src/lib/components/LogoutButton.svelte && touch src/lib/components/Profile.svelte
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType Directory -Force -Path src/lib/components
      New-Item -ItemType File -Path src/lib/components/LoginButton.svelte
      New-Item -ItemType File -Path src/lib/components/LogoutButton.svelte
      New-Item -ItemType File -Path src/lib/components/Profile.svelte
      ```
    </CodeGroup>

    次のコードスニペットを追加してください

    <AuthCodeGroup>
      ```svelte src/lib/components/LoginButton.svelte lines expandable theme={null}
      <script lang="ts">
        import { login } from '$lib/stores/auth';

        async function handleLogin() {
          await login();
        }
      </script>

      <button 
        on:click={handleLogin}
        class="button login"
      >
        Log In
      </button>

      <style>
        .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);
        }
      </style>
      ```

      ```svelte src/lib/components/LogoutButton.svelte lines expandable theme={null}
      <script lang="ts">
        import { logout } from '$lib/stores/auth';

        async function handleLogout() {
          await logout();
        }
      </script>

      <button
        on:click={handleLogout}
        class="button logout"
      >
        Log Out
      </button>

      <style>
        .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(252, 129, 129, 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);
        }
      </style>
      ```

      ```svelte src/lib/components/Profile.svelte lines expandable theme={null}
      <script lang="ts">
        import { user, isAuthenticated, isLoading } from '$lib/stores/auth';
        
        const placeholderImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`;
        
        function handleImageError(event: Event) {
          const target = event.target as HTMLImageElement;
          target.src = placeholderImage;
        }
      </script>

      {#if $isLoading}
        <div class="loading-text">Loading profile...</div>
      {:else if $isAuthenticated && $user}
        <div class="profile-container">
          <img 
            src={$user.picture || placeholderImage} 
            alt={$user.name || 'User'} 
            class="profile-picture"
            on:error={handleImageError}
          />
          <div class="profile-info">
            <div class="profile-name">
              {$user.name || 'Unknown User'}
            </div>
            <div class="profile-email">
              {$user.email || ''}
            </div>
          </div>
        </div>
      {/if}

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

        .profile-container {
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 1rem;
        }

        .profile-picture {
          width: 110px;
          height: 110px;
          border-radius: 50%;
          object-fit: cover;
          border: 3px solid #63b3ed;
          transition: transform 0.3s ease-in-out;
        }

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

        .profile-info {
          text-align: center;
        }

        .profile-name {
          font-size: 2rem;
          font-weight: 600;
          color: #f7fafc;
          margin-bottom: 0.5rem;
        }

        .profile-email {
          font-size: 1.15rem;
          color: #a0aec0;
        }

        @keyframes pulse {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.6; }
        }
      </style>
      ```

      ```svelte src/routes/+layout.svelte expandable lines theme={null}
      <script lang="ts">
        import { onMount } from 'svelte';
        import { initializeAuth } from '$lib/stores/auth';

        onMount(() => {
          initializeAuth();
        });
      </script>

      <main>
        <slot />
      </main>

      <style>
        :global(body) {
          margin: 0;
          font-family: 'Inter', sans-serif;
          background-color: #1a1e27;
          min-height: 100vh;
          color: #e2e8f0;
        }

        main {
          min-height: 100vh;
          display: flex;
          justify-content: center;
          align-items: center;
          padding: 1rem;
          box-sizing: border-box;
        }
      </style>
      ```

      ```svelte src/routes/+page.svelte expandable lines theme={null}
      <script lang="ts">
        import { isAuthenticated, isLoading, error, user } from '$lib/stores/auth';
        import LoginButton from '$lib/components/LoginButton.svelte';
        import LogoutButton from '$lib/components/LogoutButton.svelte';
        import Profile from '$lib/components/Profile.svelte';
      </script>

      <svelte:head>
        <title>Auth0 Svelte Sample</title>
        <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
      </svelte:head>

      <div class="app-container">
        {#if $isLoading}
          <div class="loading-state">
            <div class="loading-text">Loading...</div>
          </div>
        {:else if $error}
          <div class="error-state">
            <div class="error-title">Oops!</div>
            <div class="error-message">Something went wrong</div>
            <div class="error-sub-message">{$error}</div>
          </div>
        {:else}
          <div class="main-card-wrapper">
            <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>

            {#if $isAuthenticated}
              <div class="logged-in-section">
                <div class="logged-in-message">✅ Successfully authenticated!</div>
                <h2 class="profile-section-title">Your Profile</h2>
                <div class="profile-card">
                  <Profile />
                </div>
                <LogoutButton />
              </div>
            {:else}
              <div class="action-card">
                <p class="action-text">Get started by signing in to your account</p>
                <LoginButton />
              </div>
            {/if}
          </div>
        {/if}
      </div>

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

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

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

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

          .auth0-logo {
            width: 120px;
          }
        }
      </style>
      ```
    </AuthCodeGroup>
  </Step>

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

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

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

***

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

<Accordion title="ログインできない">
  ログインボタンをクリックしても何も起こらない場合:

  1. ブラウザの DevTools (F12) を開き、Console タブを確認します
  2. `.env` ファイルに有効な Auth0 の認証情報が設定されていることを確認します
  3. Auth0 アプリケーションに正しいコールバック URL が設定されていることを確認します
  4. `VITE_AUTH0_DOMAIN` には `https://` を付けず、ドメイン (例: `tenant.auth0.com`) のみを指定していることを確認します
</Accordion>

<Accordion title="認証エラー">
  **"Callback URL mismatch"**:

  * Auth0 Dashboard → Applications → Your App → Settings に移動します
  * `http://localhost:5173` を Allowed Callback URLs、Allowed Logout URLs、Allowed Web Origins に追加します
  * "Save Changes" をクリックします

  **"Invalid state"**:

  * ブラウザのキャッシュと Cookie を削除します
  * シークレットウィンドウまたはプライベートブラウズで試します
</Accordion>

<Accordion title="開発サーバーの問題">
  `npm run dev` が失敗する場合:

  * `npm run check` を実行して TypeScript のエラーを確認します
  * すべてのファイルが正しく作成されていることを確認します
  * 必要な依存関係がすべてインストールされていることを確認します: `npm install`
</Accordion>

<Tip>
  まだ解決しない場合は、[Auth0 Community](https://community.auth0.com/) または [SvelteKit Discord](https://svelte.dev/chat) を確認してください。
</Tip>
