メインコンテンツへスキップ

AI を使って Auth0 を統合する

Claude Code、Cursor、GitHub Copilot などの AI コーディングアシスタントを使用している場合は、agent skills を使って数分で Auth0 認証を自動的に追加できます。インストール:
npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-nextjs
続けて、AI アシスタントに次のように依頼します:
Add Auth0 authentication to my Next.js app
AI アシスタントは、Auth0 アプリケーションの作成、認証情報の取得、@auth0/nextjs-auth0 のインストール、API ルートの作成、環境変数の設定を自動的に行います。agent skills の詳細なドキュメント →
前提条件: 開始する前に、以下がインストールされていることを確認してください。インストールを確認するには: node --version && npm --version

はじめに

このクイックスタートでは、Next.js 16 アプリケーションに Auth0 認証を追加する方法を説明します。Auth0 Next.js SDK を使用して、サーバーサイドレンダリング、セキュアなログイン機能、保護されたルートを備えたフルスタック Web アプリケーションを構築します。
1

新しいプロジェクトを作成

このQuickstart用の新しいNext.jsプロジェクトを作成する
npx create-next-app@latest auth0-nextjs --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --yes
プロジェクトを開く
cd auth0-nextjs
2

Auth0 Next.js SDKをインストールする

shellscript npm install @auth0/nextjs-auth0
3

プロジェクトファイルを作成

Auth0 統合に必要なディレクトリとファイルをすべて作成します。
mkdir -p src/lib src/components && touch src/lib/auth0.ts src/proxy.ts src/components/LoginButton.tsx src/components/LogoutButton.tsx src/components/Profile.tsx
4

Auth0 アプリを設定する

次に、Auth0テナントで新しいアプリを作成し、プロジェクトに環境変数を追加します。Auth0アプリをセットアップするには、次の3つの方法があります。Quick Setupツールを使用する (推奨) 、CLIコマンドを実行する、またはDashboardから手動で設定する方法です。
Auth0アプリを作成し、適切な設定値があらかじめ入力された .env ファイルをコピーします。
5

Auth0 の設定を作成する

src/lib/auth0.ts に Auth0 クライアントのコードを追加します:
src/lib/auth0.ts
import { Auth0Client } from '@auth0/nextjs-auth0/server';

export const auth0 = new Auth0Client();
6

プロキシを追加

プロキシコードを src/proxy.ts に追加します:
src/proxy.ts
import { auth0 } from "./lib/auth0";

export async function proxy(request: Request) {
  return await auth0.middleware(request);
}

export const config = {
  matcher: [
    /*
     * 以下で始まるパスを除く、すべてのリクエストパスに一致:
     * - _next/static (静的ファイル)
     * - _next/image (画像最適化ファイル)
     * - favicon.ico, sitemap.xml, robots.txt (メタデータファイル)
     */
    "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
  ],
};
src/ ディレクトリを使用しているため、proxy.ts ファイルは src/ 内に作成されます。src/ ディレクトリを使用していない場合は、代わりにプロジェクトルートに作成してください。
このプロキシは、次の認証ルートを自動的にマウントします。
  • /auth/login - ログイン ルート
  • /auth/logout - ログアウト ルート
  • /auth/callback - コールバック ルート
  • /auth/profile - ユーザープロフィール ルート
  • /auth/access-token - アクセストークン ルート
  • /auth/backchannel-logout - バックチャネルログアウト ルート
7

Login、ログアウト、ユーザープロファイル用のコンポーネントを作成

ステップ 3 で作成したファイルに、次のコンポーネントコードを追加します。
8

メインページを更新

src/app/page.tsx を以下の内容に置き換えます:
src/app/page.tsx
import { auth0 } from "@/lib/auth0";
import LoginButton from "@/components/LoginButton";
import LogoutButton from "@/components/LogoutButton";
import Profile from "@/components/Profile";

export default async function Home() {
  const session = await auth0.getSession();
  const user = session?.user;

  return (
    <main className="min-h-screen bg-[#efefef] flex flex-col items-center justify-center gap-4 px-6 py-12">
      <div className="bg-white rounded-[28px] shadow-[0_4px_32px_rgba(0,0,0,0.08)] px-12 py-14 flex flex-col items-center gap-4 w-[360px]">
        <svg width="68" height="68" viewBox="0 0 68 68" fill="none" xmlns="http://www.w3.org/2000/svg" className="mb-1">
          <g filter="url(#filter0_di)">
            <rect x="2" y="2" width="64" height="64" rx="16" fill="url(#paint0_linear)"/>
            <rect x="2.5" y="2.5" width="63" height="63" rx="15.5" stroke="#252525"/>
            <path d="M34.0002 18C25.1572 18 18 25.1669 18 34C18 42.8432 25.1672 50 34.0002 50C42.8333 50 50 42.8331 50 34C50 25.1669 42.8433 18 34.0002 18ZM43.9172 43.8971C43.9172 43.9071 43.9069 43.9072 43.9069 43.9172C43.9069 43.9172 43.8969 43.9272 43.8868 43.9272C43.144 44.65 41.9796 44.7303 41.0662 44.2585L40.0228 43.7265C36.2487 41.7792 31.7619 41.7792 27.9777 43.7265L26.9338 44.2585C26.0103 44.7303 24.8459 44.65 24.1132 43.9272C24.1132 43.9272 24.1031 43.9172 24.0931 43.9172C24.0931 43.9172 24.0828 43.9071 24.0828 43.8971C23.3601 43.1543 23.2797 41.9899 23.7515 41.0765L24.2837 40.0326C26.231 36.2585 26.231 31.7717 24.2837 27.9975L23.7515 26.9536C23.2797 26.0302 23.3601 24.8657 24.0828 24.133C24.0828 24.1229 24.0931 24.123 24.0931 24.123C24.0931 24.123 24.1031 24.1129 24.1132 24.1129C24.856 23.3902 26.0204 23.3099 26.9338 23.7817L27.9777 24.3137C31.7518 26.261 36.2386 26.261 40.0228 24.3137L41.0662 23.7817C41.9897 23.3099 43.1541 23.3902 43.8868 24.1129C43.8868 24.1129 43.8969 24.123 43.9069 24.123L43.9172 24.133C44.6399 24.8758 44.7203 26.0402 44.2485 26.9536L43.7163 27.9975C41.769 31.7717 41.769 36.2585 43.7163 40.0326L44.2485 41.0765C44.7203 41.9899 44.6499 43.1543 43.9172 43.8971Z" fill="white"/>
          </g>
          <defs>
            <filter id="filter0_di" x="0" y="0" width="68" height="68" filterUnits="userSpaceOnUse" colorInterpolationFilters="sRGB">
              <feFlood floodOpacity="0" result="BackgroundImageFix"/>
              <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
              <feMorphology radius="2" operator="dilate" in="SourceAlpha" result="effect1_dropShadow"/>
              <feOffset/>
              <feComposite in2="hardAlpha" operator="out"/>
              <feColorMatrix type="matrix" values="0 0 0 0 0.117647 0 0 0 0 0.129412 0 0 0 0 0.164706 0 0 0 0.12 0"/>
              <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
              <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
              <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
              <feOffset dy="-1"/>
              <feGaussianBlur stdDeviation="0.5"/>
              <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
              <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0"/>
              <feBlend mode="normal" in2="shape" result="effect2_innerShadow"/>
            </filter>
            <linearGradient id="paint0_linear" x1="34" y1="2" x2="34" y2="66" gradientUnits="userSpaceOnUse">
              <stop/>
              <stop offset="1" stopColor="#677190"/>
            </linearGradient>
          </defs>
        </svg>

        {user ? (
          <>
            <h1 className="text-[17px] font-bold text-gray-900 tracking-tight">アカウント</h1>
            <div className="w-full h-px bg-gray-100" />
            <Profile />
            <LogoutButton />
          </>
        ) : (
          <>
            <h1 className="text-[17px] font-bold text-gray-900 tracking-tight">Sample0 へようこそ</h1>
            <p className="text-[13px] text-gray-400 text-center leading-relaxed -mt-2">
              アカウントにログインして始めましょう
            </p>
            <div className="h-3" />
            <LoginButton />
          </>
        )}
      </div>

      <div className="flex items-center gap-1.5 text-[11px] text-gray-400">
        <span>Powered by</span>
        <img
          src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-onlight.svg"
          alt="Auth0"
          className="h-3 opacity-40"
        />
      </div>
    </main>
  );
}
9

Auth0Provider を使用してレイアウトを更新する

src/app/layout.tsx を更新して Inter フォントを読み込み、アプリを Auth0Provider で囲みます。
src/app/layout.tsx
import { Inter } from "next/font/google";
import type { Metadata } from "next";
import { Auth0Provider } from "@auth0/nextjs-auth0/client";
import "./globals.css";

const inter = Inter({
  subsets: ["latin"],
  weight: ["300", "400", "500", "600", "700"],
});

export const metadata: Metadata = {
  title: "Auth0 Next.js App",
  description: "Next.js app with Auth0 authentication",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Auth0Provider>{children}</Auth0Provider>
      </body>
    </html>
  );
}
v4 では、Auth0Provider は省略可能です。必要なのは、サーバー レンダリング時に初期ユーザーを渡し、それを useUser() フックで利用できるようにする場合だけです。
10

Tailwind CSS を設定する

src/app/globals.css の内容を次の内容に置き換えます。
src/app/globals.css
@import "tailwindcss";
11

アプリを実行する

npm run dev
アプリは http://localhost:3000 で利用できます。Auth0 SDK v4 は、認証ルートを /auth/* に自動的にマウントします (v3 の /api/auth/* ではありません) 。ポート 3000 が使用中の場合は、npm run dev -- --port 3001 を実行し、Auth0 アプリの callback URL を http://localhost:3001 に更新してください。
チェックポイントこれで、Auth0 のログインページが localhost 上で正常に動作しているはずです

トラブルシューティング

JWEDecryptionFailed: decryption operation failed エラーが表示される場合は、AUTH0_SECRET が無効であるか、別のシークレットで暗号化された古いセッションクッキーが原因です。解決策:
  1. 次のコマンドで新しいシークレットを生成します:
openssl rand -hex 32
  1. .env.local ファイルを更新します:
AUTH0_SECRET=<your-new-64-character-hex-string>
  1. localhost:3000ブラウザ cookies を削除します:
    • Chrome/Edge: F12 キーを押す → [Application] タブ → [Cookies] → localhost の cookies をすべて削除
    • Firefox: F12 キーを押す → [Storage] タブ → [Cookies] → localhost の cookies をすべて削除
    • Safari: [Develop] メニュー → [Show Web Inspector] → [Storage] タブ → [Cookies] → すべて削除
  2. 開発サーバーを再起動します:
npm run dev
シークレットは必ず 32 バイト (16 進数 64 文字) である必要があります。このエラーは、アプリが別のシークレットで暗号化された既存のセッションクッキーを復号しようとしたときに発生します。
ログイン をクリックすると 404 ページが表示される場合は、次の一般的な問題を確認してください:
  1. プロキシの配置場所: src/proxy.ts が正しい場所にあることを確認します
  2. プロキシコード: プロキシが手順 6 のコードと一致していることを確認します
  3. サーバーの再起動: プロキシファイルを作成した後、開発サーバーを再起動します
  4. import の確認: import { auth0 } from "./lib/auth0" のパスが正しいことを確認します
“Cannot find module ’@/components/LoginButton’” のようなエラーが表示される場合:
  1. ファイルの存在確認: 手順 3 のファイルがすべて作成されていることを確認します
  2. パスの確認: コンポーネントが src/components/ ディレクトリにあることを確認します
  3. TypeScript の再起動: Cmd+Shift+P (Mac) または Ctrl+Shift+P (Windows) を押して、“TypeScript: Restart TS Server” を実行します
  4. import の確認: @/components/* を使用していることを確認します (~/components/* ではありません)

高度な使い方

このクイックスタートでは Auth0 Next.js SDK v4 を使用します。v3 から大きな変更があります。
  • 動的ルートハンドラーは不要 - 認証ルートはプロキシによって自動的にマウントされます
  • クライアント設定の簡素化 - new Auth0Client() は環境変数を自動的に読み込みます
  • 新しいルートパス - ルートは /api/auth/* ではなく /auth/* に配置されます
  • プロキシが必須 - すべての認証機能は proxy.ts 経由で動作します
  • <a> タグを使用 - ナビゲーションには、onClick を使うボタンではなく <a href="/auth/login"> を使用する必要があります

認証ルート

SDK は、プロキシ経由で以下のルートを自動的にマウントします。
RoutePurpose
/auth/loginログインを開始
/auth/logoutユーザーをログアウト
/auth/callbackAuth0 コールバックを処理
/auth/profileユーザープロフィールを取得
/auth/access-tokenアクセストークンを取得
/auth/backchannel-logoutバックチャネルログアウトを処理
これらのルートで 404 エラーが発生する場合は、次の点を確認してください。
  1. proxy.ts ファイルが正しい場所にあること (プロジェクトルート、または src/ ディレクトリを使用している場合は src/ 内)
  2. 手順 6 に記載の matcher パターンでプロキシが正しく設定されていること
  3. プロキシファイルの作成後に開発サーバーを再起動していること
Auth0 Next.js SDK v4 は、App Router と Pages Router の両方のパターンをサポートしています。以下は、一般的なサーバーサイドパターンの例です。
app/protected/page.tsx
import { auth0 } from "@/lib/auth0";
import { redirect } from "next/navigation";

export default async function ProtectedPage() {
  const session = await auth0.getSession();

  if (!session) {
    redirect('/auth/login');
  }

  return (
    <div>
      <h1>Protected Content</h1>
      <p>Welcome, {session.user.name}!</p>
    </div>
  );
}
クライアントサイドで認証状態を扱うには、useUser フックを使用します。
components/UserProfile.tsx
"use client";

import { useUser } from "@auth0/nextjs-auth0/client";

export default function UserProfile() {
  const { user, error, isLoading } = useUser();

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!user) return <div>Not logged in</div>;

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <img src={user.picture} alt="Profile" referrerPolicy="no-referrer" />
    </div>
  );
}
API ルートを保護するには、withApiAuthRequired メソッドを使用します。
app/api/protected/route.ts
import { auth0 } from "@/lib/auth0";

export const GET = auth0.withApiAuthRequired(async function handler() {
  const session = await auth0.getSession();

  return Response.json({
    message: "This is a protected API route",
    user: session?.user
  });
});
Auth0 の認証トークンを必要とするサードパーティのバックエンドサービス (Convex、Supabase、Firebase など) を使用している場合は、Next.js アプリからバックエンドクライアントにアクセストークンを渡す必要があります。

アクセストークンの取得

サーバーサイド (App Router) :
app/api/token/route.ts
import { auth0 } from "@/lib/auth0";
import { NextResponse } from "next/server";

export async function GET() {
  const session = await auth0.getSession();

  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const accessToken = session.accessToken;
  return NextResponse.json({ accessToken });
}
クライアントサイド:
lib/convex-client.ts
"use client";

import { ConvexProviderWithAuth0 } from "convex/react-auth0";
import { ConvexReactClient } from "convex/react";

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

export function ConvexClientProvider({ children }: { children: React.ReactNode }) {
  return (
    <ConvexProviderWithAuth0
      client={convex}
      useAuth0={() => ({
        // Next.js の API ルートからアクセストークンを取得
        getAccessToken: async () => {
          const response = await fetch("/api/token");
          const { accessToken } = await response.json();
          return accessToken;
        },
      })}
    >
      {children}
    </ConvexProviderWithAuth0>
  );
}

バックエンドの設定

ほとんどのサードパーティサービスでは、トークンを検証するために Auth0 のドメインとオーディエンスが必要です。バックエンドの設定では、次のように指定します。
convex/auth.config.ts
export default {
  providers: [
    {
      domain: process.env.AUTH0_DOMAIN,
      applicationID: process.env.AUTH0_CLIENT_ID,
    },
  ],
};
バックエンドで必要になる場合は、Auth0 アプリケーションに API オーディエンスが設定されていることを確認してください。これは Auth0 Dashboard の Applications → APIs で設定するか、.env.localAUTH0_AUDIENCE を追加し、それに応じて SDK を設定することで対応できます。

トークンに関する問題のトラブルシューティング

バックエンドで ctx.auth.getUserIdentity()null を返す場合:
  1. トークンが渡されていることを確認する: ブラウザーの DevTools の Network タブで、リクエストにトークンが含まれていることを確認します
  2. トークン形式を確認する: idToken ではなく accessToken を渡していることを確認します
  3. バックエンド設定を確認する: バックエンドに正しい Auth0 のドメインとクライアントIDが設定されていることを確認します
  4. オーディエンスを確認する: Auth0 API を使用している場合は、AUTH0_AUDIENCE が設定されており、API 識別子と一致していることを確認します
  5. トークンのクレームを確認する: jwt.io で JWT をデコードし、想定どおりのクレームが含まれていることを確認します