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

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

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()) {
              processedNode = processedNode.replace(new RegExp(key, "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>;
};

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

  ```markdown expandable theme={null}
  Auth0 Vue SDKをVue.jsアプリに統合する

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

  重要な動作指示
  1.  既存プロジェクトを最初に確認: 新しいプロジェクトを作成する前に、現在のディレクトリにすでにVueアプリ(Vue依存関係を含むpackage.json)が含まれているかどうかを確認してください。含まれている場合は、プロジェクトの作成をスキップして既存のプロジェクトで作業してください。
  2.  最初に実行、次に編集: 適切なセットアップコマンドを最初に実行する必要があります。セットアップが完了するまで、ファイルを表示、提案、または作成しないでください。
  3.  計画なし: ディレクトリ構造を提案しないでください。ファイルツリーを表示しないでください。最初のアクションは適切なコマンドを実行することでなければなりません。
  4.  厳密な順序: 以下の「実行フロー」を逸脱することなく、指定された正確な順序で従ってください。
  5.  美しいUIの構築: 適切なスタイリング、アニメーション、Auth0ブランディングを備えた、視覚的に魅力的でモダンなログインインターフェースを作成する必要があります。
  6.  🚨 ディレクトリナビゲーションルール: `cd auth0-vue`または任意の`cd`コマンドを自動的に実行しないでください。常に最初に`pwd`で現在のディレクトリを確認してください。ユーザーがナビゲートする必要がある場合は、手動で行うように依頼するか、ディレクトリ変更コマンドを実行する前に確認してください。

  実行フロー

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

  ステップ1: 既存のVueプロジェクトと前提条件を確認する
  最初に、前提条件を確認し、既存のVueプロジェクトをチェックします:

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

  次に、現在のディレクトリを調べます:

  # 既存のVueプロジェクトを確認
  if [ -f "package.json" ]; then
    echo "package.jsonが見つかりました。Vue依存関係を確認しています..."
    cat package.json
  else
    echo "package.jsonが見つかりません。新しいプロジェクトを作成します"
  fi

  結果に基づいて:
  - package.jsonが存在し、Vue依存関係が含まれている場合は、ステップ1b(Auth0 SDKのみをインストール)に進みます
  - Vueプロジェクトが存在しない場合は、ステップ1a(新しいプロジェクトを作成)に進みます

  ステップ1a: 新しいプロジェクトを作成してVue SDKをインストールする
  既存のプロジェクトが存在する場合は、単にSDKをインストールします:
  npm install @auth0/auth0-vue@latest
  それ以外の場合は、新しいプロジェクトを作成してSDKをインストールします:

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

  npm create vue@latest auth0-vue -- --typescript --router --pinia && cd auth0-vue && npm install && npm add @auth0/auth0-vue


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

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

  2.1: Auth0環境設定をセットアップする

  ⚠️ 重要: 続行する前に、現在のディレクトリを確認してください:
  - 新しいプロジェクトを作成したばかりの場合: auth0-vueディレクトリ内にいる必要があります
  - 既存のプロジェクトで作業している場合: プロジェクトのルートディレクトリにいる必要があります
  - `cd auth0-vue`コマンドを実行しないでください - 最初に正しいディレクトリにナビゲートしてください

  ステップ2.1a: プロジェクトディレクトリにナビゲート(必要な場合)してAuth0をセットアップする:

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

  次に、お使いのOSの環境セットアップコマンドを実行します:

  ⚠️ 重要なディレクトリ検証ステップ:
  Auth0 CLIセットアップコマンドを実行する前に、以下を実行する必要があります:

  pwd && ls -la

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

  MacOSの場合、以下のコマンドを実行します:
  AUTH0_APP_NAME="My Vue App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t spa -c http://localhost:5173 -l http://localhost:5173 -o http://localhost:5173 --json > auth0-app-details.json && CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) && DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && echo "VITE_AUTH0_DOMAIN=${DOMAIN}" > .env && echo "VITE_AUTH0_CLIENT_ID=${CLIENT_ID}" >> .env && rm auth0-app-details.json && echo ".env file created with your Auth0 details:" && cat .env

  Windowsの場合、以下のコマンドを実行します:
  $AppName = "My Vue App"; winget install Auth0.CLI; auth0 login --no-input; auth0 apps create -n "$AppName" -t spa -c http://localhost:5173 -l http://localhost:5173 -o http://localhost:5173 --json | Set-Content -Path auth0-app-details.json; $ClientId = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_id; $Domain = (auth0 tenants list --json | ConvertFrom-Json | Where-Object { $_.active -eq $true }).name; Set-Content -Path .env -Value "VITE_AUTH0_DOMAIN=$Domain"; Add-Content -Path .env -Value "VITE_AUTH0_CLIENT_ID=$ClientId"; Remove-Item auth0-app-details.json; Write-Output ".env file created with your Auth0 details:"; Get-Content .env


  ステップ2.1b: 手動の.envテンプレートを作成する(自動セットアップが失敗した場合)

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

  ステップ2.1c: 手動セットアップ手順を表示する

  echo "📋 手動セットアップが必要です:"
  echo "1. https://manage.auth0.com/dashboard/ にアクセスしてください"
  echo "2. 'アプリケーションを作成' → シングルページアプリケーション をクリックしてください"
  echo "3. 許可されたコールバックURLを設定: http://localhost:5173"
  echo "4. 許可されたログアウトURLを設定: http://localhost:5173"
  echo "5. 許可されたWebオリジンを設定: http://localhost:5173"
  echo "6. ドメインとクライアントIDで.envファイルを更新してください"

  2.2: 適切なAuth0設定でsrc/main.tsを更新する
  src/main.tsの内容全体を置き換えます(存在しない場合は作成します):

  import './assets/main.css'

  import { createApp } from 'vue'
  import { createPinia } from 'pinia'
  import router from './router'
  import { createAuth0 } from '@auth0/auth0-vue'

  import App from './App.vue'

  const domain = import.meta.env.VITE_AUTH0_DOMAIN
  const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID

  // Auth0設定を検証
  if (!domain || !clientId) {
    console.error('Auth0設定が見つかりません。.envファイルを確認してください。')
    console.error('必要な環境変数:')
    console.error('- VITE_AUTH0_DOMAIN')
    console.error('- VITE_AUTH0_CLIENT_ID')
    throw new Error('Auth0ドメインとクライアントIDは.envファイルに設定する必要があります')
  }

  // ドメイン形式を検証
  if (!domain.includes('.auth0.com') && !domain.includes('.us.auth0.com') && !domain.includes('.eu.auth0.com') && !domain.includes('.au.auth0.com')) {
    console.warn('Auth0ドメイン形式が正しくない可能性があります。期待される形式: your-domain.auth0.com')
  }

  const app = createApp(App)

  app.use(createAuth0({
    domain: domain,
    clientId: clientId,
    authorizationParams: {
      redirect_uri: window.location.origin
    }
  }))

  app.use(createPinia())
  app.use(router)

  app.mount('#app')

  2.3: 認証コンポーネントを作成する
  最初にコンポーネントファイルを作成します:

  touch src/components/LoginButton.vue && touch src/components/LogoutButton.vue && touch src/components/UserProfile.vue

  2.4: LoginButtonコンポーネントを作成する
  このコードでsrc/components/LoginButton.vueを作成します:

  ⚠️ VUEコンポーネントガイドライン:
  - モダンなVue 3構文のために`<script setup>`でComposition APIを使用してください
  - 認証状態のために'@auth0/auth0-vue'からuseAuth0をインポートしてください
  - スクリプトタグで`lang="ts"`を使用して適切なTypeScriptサポートを確保してください

  <template>
    <button 
      @click="handleLogin" 
      class="button login"
      :disabled="isLoading"
    >
      {{ isLoading ? '読み込み中...' : 'ログイン' }}
    </button>
  </template>

  <script setup lang="ts">
  import { useAuth0 } from '@auth0/auth0-vue'

  const { loginWithRedirect, isLoading } = useAuth0()

  const handleLogin = () => {
    loginWithRedirect()
  }
  </script>

  2.5: LogoutButtonコンポーネントを作成する  
  次のコードでsrc/components/LogoutButton.vueを作成します:

  <template>
    <button
      @click="handleLogout"
      class="button logout"
      :disabled="isLoading"
    >
      {{ isLoading ? '読み込み中...' : 'ログアウト' }}
    </button>
  </template>

  <script setup lang="ts">
  import { useAuth0 } from '@auth0/auth0-vue'

  const { logout, isLoading } = useAuth0()

  const handleLogout = () => {
    logout({
      logoutParams: {
        returnTo: window.location.origin
      }
    })
  }
  </script>

  2.6: UserProfileコンポーネントを作成する
  次のコードでsrc/components/UserProfile.vueを作成します:

  <template>
    <div v-if="isLoading" class="loading-text">
      プロファイルを読み込み中...
    </div>
    <div 
      v-else-if="isAuthenticated && user" 
      class="profile-container"
    >
      <img 
        :src="user.picture || placeholderImage" 
        :alt="user.name || 'User'" 
        class="profile-picture"
        @error="handleImageError"
      />
      <div class="profile-info">
        <div class="profile-name">
          {{ user.name }}
        </div>
        <div class="profile-email">
          {{ user.email }}
        </div>
      </div>
    </div>
  </template>

  <script setup lang="ts">
  import { useAuth0 } from '@auth0/auth0-vue'

  const { user, isAuthenticated, isLoading } = useAuth0()

  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(e: Event) {
    const target = e.target as HTMLImageElement
    target.src = placeholderImage
  }
  </script>

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

  2.7: App.vueを美しくモダンなUIに更新する
  既存のsrc/App.vueファイルの内容全体を、適切なスタイリングとコンポーネントを含む次のコードに置き換えます:

  <template>
    <div class="app-container">
      <div v-if="isLoading" class="loading-state">
        <div class="loading-text">読み込み中...</div>
      </div>
      
      <div v-else-if="error" class="error-state">
        <div class="error-title">エラーが発生しました!</div>
        <div class="error-message">問題が発生しました</div>
        <div class="error-sub-message">{{ error.message }}</div>
      </div>
      
      <div v-else 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"
          @error="handleImageError"
        />
        <h1 class="main-title">Vue0へようこそ</h1>
        
        <div v-if="isAuthenticated" class="logged-in-section">
          <div class="logged-in-message">✅ 認証に成功しました!</div>
          <h2 class="profile-section-title">プロファイル</h2>
          <div class="profile-card">
            <UserProfile />
          </div>
          <LogoutButton />
        </div>
        
        <div v-else class="action-card">
          <p class="action-text">アカウントにサインインして開始してください</p>
          <LoginButton />
        </div>
      </div>
    </div>
  </template>

  <script setup lang="ts">
  import { useAuth0 } from '@auth0/auth0-vue'
  import LoginButton from './components/LoginButton.vue'
  import LogoutButton from './components/LogoutButton.vue'
  import UserProfile from './components/UserProfile.vue'

  const { isAuthenticated, isLoading, error } = useAuth0()

  const handleImageError = (e: Event) => {
    const target = e.target as HTMLImageElement
    target.style.display = 'none'
  }
  </script>

  2.8: assets/main.cssに美しくモダンなCSSスタイリングを追加する
  src/assets/main.cssの内容全体を、次のモダンなAuth0ブランドのスタイリングに置き換えます:

  ⚠️ CSSファイル置き換え方法:
  既存のmain.cssファイルが大きい場合や形式が正しくない場合は、まず新しい一時的なCSSファイル(例: main-new.css)を作成してから、`mv src/assets/main-new.css src/assets/main.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 {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .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:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    transform: none;
  }

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

  .button.login:hover:not(:disabled) {
    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:not(:disabled) {
    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;
  }

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

  2.9: 開発サーバーを起動する

  ⚠️ 重要: ターミナル作業ディレクトリの問題
  統合プロセス中にターミナルの作業ディレクトリが混乱することがあります。開発サーバーを起動する前に、必ず以下を実行してください:

  1. 明示的なパス変更で正しいディレクトリにいることを確認する:

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

  2. 現在のディレクトリにpackage.jsonがあることを確認する:

  ls -la package.json

  3. npmコマンドが「package.json not found」で失敗する場合は、絶対パスメソッドを使用する:

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

  ⚠️ 重要: `pwd`が正しいディレクトリを表示していても、コマンド実行時のターミナルの作業ディレクトリが異なる場合があります。npmコマンドを実行する前に、必ず絶対パスで明示的に`cd`を使用して、正しい場所で実行されることを確認してください。

  ステップ2.9a: カスタムポートが必要かどうかを確認する

  grep -q "VITE_DEV_PORT" .env 2>/dev/null && echo "カスタムポートが検出されました" || echo "デフォルトポートを使用します"

  ステップ2.9b: 適切なポートで開発サーバーを起動する

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

  # カスタムポートを使用する場合(ステップ2.9aでカスタムポートが検出された場合は、代わりにこれを実行):
  npm run dev -- --port 5174

  ⚠️ Vueコンポーネントインポートエラーのトラブルシューティング:
  ファイルが存在するにもかかわらず「Cannot resolve component」エラーが表示される場合:
  1. ファイルは正しく作成されています - これは多くの場合、VS Code Vueランゲージサービスのキャッシュの問題です
  2. これらのエラーにもかかわらず、開発サーバーは正常に動作するはずです
  3. ブラウザでアプリケーションを開いて機能を確認してください
  4. エラーは通常、Vueランゲージサービスが更新された後に自動的に解決されます

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

  AIコード生成の絶対要件
  1.  @auth0/auth0-vueパッケージを使用する必要があります。
  2.  環境変数から認証情報を取得する必要があります(例: import.meta.env.VITE_AUTH0_DOMAIN)。
  3.  main.tsのアプリレベルでcreateAuth0プラグインをインストールする必要があります。
  4.  createAuth0()設定にredirect_uriを含むauthorizationParamsを含める必要があります。
  5.  VueコンポーネントでuseAuth0コンポーザブルを使用して機能を実証する必要があります。

  統合中に発生する一般的な問題

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

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

  問題3: Vueコンポーネントインポートエラー
  問題: VS Codeが作成されたコンポーネントに対して「Cannot resolve component」エラーを表示します
  解決策: これらは通常キャッシュの問題です - アプリは正常に動作します。テスト前にすべてのコンポーネントを作成してください。

  問題4: CSSファイルの破損
  問題: 大規模なCSS置換によりファイルが破損する可能性があります
  解決策: 最初に一時的なCSSファイルを作成し、次に`mv`コマンドを使用して元のファイルを置き換えてください

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

  cd auth0-vue && npm run dev

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

  問題6: Vue 3 Composition API構文
  問題: `<script setup>`を使用したComposition APIではなくOptions APIを使用している
  解決策: 適切なTypeScriptサポートを備えた最新のVue 3開発では、常に`<script setup lang="ts">`構文を使用してください

  問題7: Vite環境変数
  問題: 環境変数にimport.meta.envではなくprocess.envを使用している
  解決策: ViteとVue 3では、環境変数にprocess.envではなくimport.meta.env.VITE_*を使用します
  ```
</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 以上
  * **[jq](https://jqlang.org/)** - Auth0 CLI のセットアップに必要

  **Vue のバージョン互換性:** このクイックスタートは **Vue 3.x** 以降で動作します。
</Note>

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

このクイックスタートでは、Vue.js 3 アプリケーションに Auth0 認証を統合する方法を紹介します。Vue の Composition API と Auth0 Vue ソフトウェア開発キット (SDK) を使用して、安全なユーザー認証を備えたレスポンシブなシングルページアプリケーションを構築します。

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

    ```shellscript theme={null}
    npm create vue@latest auth0-vue -- --typescript --router --pinia
    ```

    プロジェクトを開く

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

  <Step title="Auth0 Vue ソフトウェア開発キット (SDK) のインストール" stepNumber={2}>
    ```shellscript theme={null}
    npm add @auth0/auth0-vue && npm install
    ```
  </Step>

  <Step title="Auth0 アプリケーションを設定する" stepNumber={3}>
    Next up, you need to create a new app on your Auth0 tenant and add the environment variables to your project.

    You can choose to do this automatically by running a CLI command or do it manually via the Dashboard:

    <Tabs>
      <Tab title="CLI">
        Run the following shell command on your project's root directory to create an Auth0 app and generate a `.env` file:

        <AuthCodeGroup>
          ```shellscript Mac theme={null}
          AUTH0_APP_NAME="My Vue App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t spa -c http://localhost:5173 -l http://localhost:5173 -o http://localhost:5173 --json > auth0-app-details.json && CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) && DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && echo "VITE_AUTH0_DOMAIN=${DOMAIN}" > .env && echo "VITE_AUTH0_CLIENT_ID=${CLIENT_ID}" >> .env && rm auth0-app-details.json && echo ".env file created with your Auth0 details:" && cat .env
          ```

          ```shellscript Windows theme={null}
          $AppName = "My Vue App"; winget install Auth0.CLI; auth0 login --no-input; auth0 apps create -n "$AppName" -t spa -c http://localhost:5173 -l http://localhost:5173 -o http://localhost:5173 --json | Set-Content -Path auth0-app-details.json; $ClientId = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_id; $Domain = (auth0 tenants list --json | ConvertFrom-Json | Where-Object { $_.active -eq $true }).name; Set-Content -Path .env -Value "VITE_AUTH0_DOMAIN=$Domain"; Add-Content -Path .env -Value "VITE_AUTH0_CLIENT_ID=$ClientId"; Remove-Item auth0-app-details.json; Write-Output ".env file created with your Auth0 details:"; Get-Content .env
          ```
        </AuthCodeGroup>
      </Tab>

      <Tab title="Dashboard">
        Before you start, create a `.env` file on your project's root directory

        ```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` を、ダッシュボードに表示されている **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="Auth0 プラグインの設定" stepNumber={4}>
    ```typescript src/main.ts {6,11,12,13,14,15,16,17} lines theme={null}
    import './assets/main.css'

    import { createApp } from 'vue'
    import { createPinia } from 'pinia'
    import router from './router'
    import { createAuth0 } from '@auth0/auth0-vue'

    import App from './App.vue'

    const app = createApp(App)

    app.use(createAuth0({
      domain: import.meta.env.VITE_AUTH0_DOMAIN,
      clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
      authorizationParams: {
        redirect_uri: window.location.origin
      }
    }))

    app.use(createPinia())
    app.use(router)

    app.mount('#app')
    ```
  </Step>

  <Step title="認証コンポーネントの作成" stepNumber={5}>
    コンポーネントファイルを作成する

    ```shellscript theme={null}
    touch src/components/LoginButton.vue && touch src/components/LogoutButton.vue && touch src/components/UserProfile.vue
    ```

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

    <AuthCodeGroup>
      ```vue src/components/LoginButton.vue lines theme={null}
      <template>
        <button 
          @click="handleLogin" 
          class="button login"
          :disabled="isLoading"
        >
          {{ isLoading ? '読み込み中...' : 'ログイン' }}
        </button>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'

      const { loginWithRedirect, isLoading } = useAuth0()

      const handleLogin = () => {
        loginWithRedirect()
      }
      </script>
      ```

      ```vue src/components/LogoutButton.vue lines theme={null}
      <template>
        <button
          @click="handleLogout"
          class="button logout"
          :disabled="isLoading"
        >
          {{ isLoading ? '読み込み中...' : 'ログアウト' }}
        </button>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'

      const { logout, isLoading } = useAuth0()

      const handleLogout = () => {
        logout({
          logoutParams: {
            returnTo: window.location.origin
          }
        })
      }
      </script>
      ```

      ```vue src/components/UserProfile.vue expandable lines theme={null}
      <template>
        <div v-if="isLoading" class="loading-text">
          プロファイルを読み込み中...
        </div>
        <div 
          v-else-if="isAuthenticated && user" 
          class="profile-container"
        >
          <img 
            :src="user.picture || placeholderImage" 
            :alt="user.name || 'ユーザー'" 
            class="profile-picture"
            @error="handleImageError"
          />
          <div class="profile-info">
            <div class="profile-name">
              {{ user.name }}
            </div>
            <div class="profile-email">
              {{ user.email }}
            </div>
          </div>
        </div>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'

      const { user, isAuthenticated, isLoading } = useAuth0()

      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(e: Event) {
        const target = e.target as HTMLImageElement
        target.src = placeholderImage
      }
      </script>

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

      ```vue src/App.vue expandable lines theme={null}
      <template>
        <div class="app-container">
          <div v-if="isLoading" class="loading-state">
            <div class="loading-text">読み込み中...</div>
          </div>
          
          <div v-else-if="error" class="error-state">
            <div class="error-title">エラー</div>
            <div class="error-message">問題が発生しました</div>
            <div class="error-sub-message">{{ error.message }}</div>
          </div>
          
          <div v-else class="main-card-wrapper">
            <img 
              src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png" 
              alt="Auth0ロゴ" 
              class="auth0-logo"
              @error="handleImageError"
            />
            <h1 class="main-title">Vue0へようこそ</h1>
            
            <div v-if="isAuthenticated" class="logged-in-section">
              <div class="logged-in-message">✅ 認証に成功しました!</div>
              <h2 class="profile-section-title">あなたのプロファイル</h2>
              <div class="profile-card">
                <UserProfile />
              </div>
              <LogoutButton />
            </div>
            
            <div v-else class="action-card">
              <p class="action-text">アカウントにログインして始めましょう</p>
              <LoginButton />
            </div>
          </div>
        </div>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'
      import LoginButton from './components/LoginButton.vue'
      import LogoutButton from './components/LogoutButton.vue'
      import UserProfile from './components/UserProfile.vue'

      const { isAuthenticated, isLoading, error } = useAuth0()

      const handleImageError = (e: Event) => {
        const target = e.target as HTMLImageElement
        target.style.display = 'none'
      }
      </script>

      <style>
      @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 {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .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:disabled {
        opacity: 0.6;
        cursor: not-allowed;
        transform: none;
      }

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

      .button.login:hover:not(:disabled) {
        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:not(:disabled) {
        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;
      }

      @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;
        }
      }
      </style>
      ```
    </AuthCodeGroup>
  </Step>

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

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

  これで、正常に動作する Auth0 ログインページが [localhost](http://localhost:5173/) で稼働しているはずです。
</Check>

***

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

<Accordion title="Vue Router でルートを保護する">
  Vue Router のナビゲーションガードを使用して特定のルートを保護します：

  ```typescript src/router/index.ts theme={null}
  import { createRouter, createWebHistory } from 'vue-router'
  import { authGuard } from '@auth0/auth0-vue'
  import Home from '../views/Home.vue'
  import Profile from '../views/Profile.vue'
  import Dashboard from '../views/Dashboard.vue'

  const routes = [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/profile',
      name: 'Profile',
      component: Profile,
      beforeEnter: authGuard
    },
    {
      path: '/dashboard',
      name: 'Dashboard',
      component: Dashboard,
      beforeEnter: authGuard
    }
  ]

  const router = createRouter({
    history: createWebHistory(),
    routes
  })

  export default router
  ```
</Accordion>

<Accordion title="保護された API の呼び出し">
  Auth0 プラグインを設定して API のオーディエンスを指定し、認証済みリクエストを送信できるようにします：

  ```typescript src/main.ts theme={null}
  import { createAuth0 } from '@auth0/auth0-vue'

  const app = createApp(App)

  app.use(
    createAuth0({
      domain: import.meta.env.VITE_AUTH0_DOMAIN,
      clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
      authorizationParams: {
        redirect_uri: window.location.origin,
        audience: "YOUR_API_IDENTIFIER"
      }
    })
  )
  ```

  その後、コンポーネント内で認証済みの API 呼び出しを実行します：

  ```vue src/components/ApiCall.vue theme={null}
  <template>
    <div>
      <button @click="callProtectedApi" :disabled="isLoading">
        保護された API を呼び出す
      </button>
      <pre v-if="apiResponse">{{ JSON.stringify(apiResponse, null, 2) }}</pre>
    </div>
  </template>

  <script setup lang="ts">
  import { ref } from 'vue'
  import { useAuth0 } from '@auth0/auth0-vue'

  const { getAccessTokenSilently } = useAuth0()
  const apiResponse = ref(null)
  const isLoading = ref(false)

  const callProtectedApi = async () => {
    try {
      isLoading.value = true
      const token = await getAccessTokenSilently()
      
      const response = await fetch('/api/protected', {
        headers: {
          Authorization: `Bearer ${token}`
        }
      })
      
      apiResponse.value = await response.json()
    } catch (error) {
      console.error('API call failed:', error)
    } finally {
      isLoading.value = false
    }
  }
  </script>
  ```
</Accordion>

<Accordion title="認証ロジック用の Composable を使用する">
  よくある認証パターン向けの再利用可能な Composable を作成します：

  ```typescript src/composables/useAuthenticatedUser.ts theme={null}
  import { computed, ref, watchEffect } from 'vue'
  import { useAuth0 } from '@auth0/auth0-vue'

  export function useAuthenticatedUser() {
    const { user, isAuthenticated, isLoading, getAccessTokenSilently } = useAuth0()
    const accessToken = ref<string | null>(null)

    watchEffect(async () => {
      if (isAuthenticated.value && !isLoading.value) {
        try {
          accessToken.value = await getAccessTokenSilently()
        } catch (error) {
          console.error('Failed to get access token:', error)
        }
      }
    })

    return {
      user: computed(() => user.value),
      accessToken: computed(() => accessToken.value),
      isAuthenticated: computed(() => isAuthenticated.value),
      isLoading: computed(() => isLoading.value)
    }
  }
  ```

  コンポーネントでの使用例：

  ```vue src/components/UserDashboard.vue theme={null}
  <template>
    <div v-if="!isLoading">
      <h1>ようこそ、{{ user?.name }}</h1>
      <p>アクセストークン: {{ accessToken ? '利用可能' : '利用不可' }}</p>
    </div>
    <div v-else>読み込み中...</div>
  </template>

  <script setup lang="ts">
  import { useAuthenticatedUser } from '../composables/useAuthenticatedUser'

  const { user, accessToken, isLoading } = useAuthenticatedUser()
  </script>
  ```
</Accordion>
