> ## 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.

# Nuxt.js アプリケーションにログイン機能を追加する

> このガイドでは、Auth0 Nuxt.js SDK を使用して、Nuxt.js を利用したシングルページアプリケーション（SPA）に Auth0 を統合し、認証を追加して、ユーザープロファイル情報を表示する方法を説明します。

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

<HowToSchema />

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

<Accordion title="AI を使って Auth0 を統合する" icon="microchip-ai" iconType="solid" defaultOpen>
  Claude Code、Cursor、GitHub Copilot などの AI コーディングアシスタントを使用している場合は、[agent skills](https://agentskills.io/home) を使うことで、数分で Auth0 認証を自動的に追加できます。

  **インストール:**

  ```bash theme={null}
  npx skills add https://github.com/auth0/agent-skills --skill auth0-nuxt
  ```

  **次に、AI アシスタントに次のように依頼します。**

  ```text theme={null}
  Add Auth0 authentication to my Nuxt app
  ```

  AI アシスタントが、Auth0 アプリケーションの作成、認証情報の取得、`@auth0/auth0-nuxt` のインストール、モジュールの設定、ルートのセットアップを自動的に行います。[agent skills の完全なドキュメント →](/ja/docs/quickstart/agent-skills)
</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 のセットアップに必要です

  **Nuxt のバージョン互換性:** このクイックスタートは **Nuxt 3.x** ではそのまま動作します。**Nuxt 4.x** の場合は、Nuxt 4.2 以降を使用していることを確認してください。
</Note>

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

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

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

    ```shellscript theme={null}
    npx nuxi@latest init auth0-nuxt-app
    ```

    プロジェクトを開く

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

  <Step title="Auth0 Nuxt SDKをインストールする" stepNumber={2}>
    ```shellscript theme={null}
    npm add @auth0/auth0-nuxt && npm install
    ```
  </Step>

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

    CLIコマンドを実行して自動的に行う方法と、Dashboardから手動で行う方法のいずれかを選択できます。

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

        <CodeGroup>
          ```shellscript Mac theme={null}
          AUTH0_APP_NAME="My Nuxt App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t regular -c http://localhost:3000/auth/callback -l http://localhost:3000 -o http://localhost:3000 --reveal-secrets --json > auth0-app-details.json && CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) && CLIENT_SECRET=$(jq -r '.client_secret' auth0-app-details.json) && DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && echo "NUXT_AUTH0_DOMAIN=${DOMAIN}" > .env && echo "NUXT_AUTH0_CLIENT_ID=${CLIENT_ID}" >> .env && echo "NUXT_AUTH0_CLIENT_SECRET=${CLIENT_SECRET}" >> .env && echo "NUXT_AUTH0_SESSION_SECRET=$(openssl rand -hex 64)" >> .env && echo "NUXT_AUTH0_APP_BASE_URL=http://localhost:3000" >> .env && rm auth0-app-details.json && echo ".env file created with your Auth0 details:" && cat .env
          ```

          ```shellscript Windows theme={null}
          $AppName = "My Nuxt App"; winget install Auth0.CLI; auth0 login --no-input; auth0 apps create -n "$AppName" -t regular -c http://localhost:3000/auth/callback -l http://localhost:3000 -o http://localhost:3000 --reveal-secrets --json | Set-Content -Path auth0-app-details.json; $ClientId = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_id; $ClientSecret = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_secret; $Domain = (auth0 tenants list --json | ConvertFrom-Json | Where-Object { $_.active -eq $true }).name; Set-Content -Path .env -Value "NUXT_AUTH0_DOMAIN=$Domain"; Add-Content -Path .env -Value "NUXT_AUTH0_CLIENT_ID=$ClientId"; Add-Content -Path .env -Value "NUXT_AUTH0_CLIENT_SECRET=$ClientSecret"; $SessionSecret = -join ((1..128) | ForEach {'{0:X}' -f (Get-Random -Max 16)}); Add-Content -Path .env -Value "NUXT_AUTH0_SESSION_SECRET=$SessionSecret"; Add-Content -Path .env -Value "NUXT_AUTH0_APP_BASE_URL=http://localhost:3000"; Remove-Item auth0-app-details.json; Write-Output ".env file created with your Auth0 details:"; Get-Content .env
          ```
        </CodeGroup>
      </Tab>

      <Tab title="ダッシュボード">
        始める前に、プロジェクトのルートディレクトリに `.env` ファイルを作成してください

        ```shellscript .env theme={null}
        NUXT_AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
        NUXT_AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
        NUXT_AUTH0_CLIENT_SECRET=YOUR_AUTH0_APP_CLIENT_SECRET
        NUXT_AUTH0_SESSION_SECRET=YOUR_SESSION_SECRET
        NUXT_AUTH0_APP_BASE_URL=http://localhost:3000
        ```

        1. [Auth0 Dashboard ](https://manage.auth0.com/dashboard/)に移動します
        2. **Applications** > **Applications** > **Create Application** をクリックします
        3. ポップアップでアプリの名前を入力し、アプリケーションタイプとして `Regular Web Application` を選択して **Create** をクリックします
        4. Application Details ページで **Settings** タブに切り替えます
        5. `.env` ファイルの値を、ダッシュボードに表示される **ドメイン**、**クライアントID**、**クライアントシークレット** の値に置き換えます
        6. `openssl rand -hex 64` を実行してセッションシークレットを生成し、それを `NUXT_AUTH0_SESSION_SECRET` に使用します

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

        **Allowed Callback URLs:**

        ```
        http://localhost:3000/auth/callback
        ```

        **許可されているログアウト URL:**

        ```
        http://localhost:3000
        ```

        **許可された Web オリジン:**

        ```
        http://localhost:3000
        ```

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

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

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

  <Step title="Auth0 Nuxt モジュールを設定する" stepNumber={4}>
    ```typescript nuxt.config.ts {3,4,5,6,7,8,9,10,11,12,13} lines theme={null}
    export default defineNuxtConfig({
      devtools: { enabled: true },
      modules: ['@auth0/auth0-nuxt'],
      runtimeConfig: {
        auth0: {
          domain: process.env.NUXT_AUTH0_DOMAIN,
          clientId: process.env.NUXT_AUTH0_CLIENT_ID,
          clientSecret: process.env.NUXT_AUTH0_CLIENT_SECRET,
          sessionSecret: process.env.NUXT_AUTH0_SESSION_SECRET,
          appBaseUrl: process.env.NUXT_AUTH0_APP_BASE_URL,
        },
      },
    })
    ```
  </Step>

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

    ```shellscript theme={null}
    mkdir -p app/components && touch app/components/LoginButton.vue && touch app/components/LogoutButton.vue && touch app/components/UserProfile.vue
    ```

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

    <CodeGroup>
      ```vue app/components/LoginButton.vue expandable lines theme={null}
      <template>
       <a 
          href="/auth/login" 
          class="button login"
        >
          Log In
        </a>
      </template>

      <style scoped>
      .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:not(:disabled) {
        background-color: #4299e1;
        transform: translateY(-5px) scale(1.03);
        box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
      }

      .button:disabled {
        opacity: 0.7;
        cursor: not-allowed;
        transform: none;
      }
      </style>
      ```

      ```vue app/components/LogoutButton.vue expandable lines theme={null}
      <template>
       <a
          href="/auth/logout"
          class="button logout"
        >
          Log Out
        </a>
      </template>

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

      .button:disabled {
        opacity: 0.7;
        cursor: not-allowed;
        transform: none;
      }
      </style>
      ```

      ```vue app/components/UserProfile.vue expandable lines theme={null}
      <template>
        <div v-if="pending" class="loading-text">
          Loading profile...
        </div>
        <div v-else-if="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">
      const user = useUser()
      const pending = ref(false)

      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
      }

      // UXを向上させるためにローディング状態をシミュレートする
      onMounted(() => {
        pending.value = true
        nextTick(() => {
          pending.value = false
        })
      })
      </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;
      }

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

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

      ```vue app.vue expandable lines theme={null}
      <template>
        <div class="app-container">
          <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 Auth0 Nuxt</h1>

            <div v-if="user" class="logged-in-section">
              <div class="logged-in-message">✅ Successfully authenticated!</div>
              <h2 class="profile-section-title">Your Profile</h2>
              <div class="profile-card">
                <UserProfile />
              </div>
              <LogoutButton />
            </div>

            <div v-else class="action-card">
              <p class="action-text">Get started by signing in to your account</p>
              <LoginButton />
            </div>
          </div>
        </div>
      </template>


      <script setup lang="ts">
      const user = useUser()
      </script>

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

      body {
        margin: 0;
        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;
        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>
      ```
    </CodeGroup>
  </Step>

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

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

  これで、[localhost](http://localhost:3000/) で Auth0 のログインページが問題なく動作しているはずです
</Check>
