> ## 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 auth0/agent-skills --skill auth0
  ```

  **次に、AI アシスタントにこう依頼します:**

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

  AI アシスタントが Auth0 アプリケーションを自動で作成し、資格情報を取得して、`@auth0/auth0-nuxt` をインストールし、モジュールを設定し、ルートをセットアップします。[agent skills の完全なドキュメント →](/docs/ja-jp/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}>
    このQuickstart向けに新しい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 App をセットアップする" stepNumber={3}>
    次に、Auth0 テナントで新しいアプリを作成し、プロジェクトに環境変数を追加します。

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

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

        <CodeGroup>
          ```shellscript Mac theme={null}
          # Auth0 CLI をインストールする（まだインストールされていない場合）
          brew tap auth0/auth0-cli && brew install auth0

          # Auth0 App をセットアップして .env ファイルを生成する
          auth0 qs setup --app --type regular --framework nuxt --port 3000 --name "My Nuxt App"
          ```

          ```powershell Windows theme={null}
          # Auth0 CLI をインストールする（まだインストールされていない場合）
          scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
          scoop install auth0

          # Auth0 App をセットアップして .env ファイルを生成する
          auth0 qs setup --app --type regular --framework nuxt --port 3000 --name "My Nuxt App"
          ```
        </CodeGroup>

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

          1. 認証済みかどうかを確認します (必要に応じてログインを求められます)
          2. `http://localhost:3000` 用に設定された Auth0 Regular Web Application を作成します
          3. `NUXT_AUTH0_DOMAIN`、`NUXT_AUTH0_CLIENT_ID`、`NUXT_AUTH0_CLIENT_SECRET`、`NUXT_AUTH0_SESSION_SECRET`、`NUXT_AUTH0_APP_BASE_URL` を含む `.env` ファイルを生成します
        </Note>
      </Tab>

      <Tab title="Auth0 Dashboard">
        始める前に、プロジェクトのルートディレクトリに `.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. **アプリケーション**  > **アプリケーション** > **Create Application** をクリックします
        3. ポップアップでアプリの名前を入力し、アプリの種類として `Regular Web Application` を選択して、**Create** をクリックします
        4. アプリケーションの詳細ページで **設定** タブに切り替えます
        5. `.env` ファイルの値を、Auth0 Dashboard の **Domain**、**Client ID**、**Client Secret** の値に置き換えます
        6. `openssl rand -hex 64` を実行してセッションシークレットを生成し、`NUXT_AUTH0_SESSION_SECRET` に使用します

        最後に、アプリケーションの詳細ページの **設定** タブで、次の 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="Login、Logout、Profile コンポーネントを作成" 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>
