Skip to main content

Use AI to integrate Auth0

If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using agent skills.Install:
npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-nextjs
Then ask your AI assistant:
Add Auth0 authentication to my Next.js app
Your AI assistant will automatically create your Auth0 application, fetch credentials, install @auth0/nextjs-auth0, create API routes, and set up environment variables. Full agent skills documentation →
Prerequisites: Before you begin, ensure you have the following installed:Verify installation: node --version && npm --version

Get Started

This quickstart demonstrates how to add Auth0 authentication to a Next.js 16 application. You’ll build a full-stack web application with server-side rendering, secure login functionality, and protected routes using the Auth0 Next.js SDK.
1

Create a new project

Create a new Next.js project for this Quickstart
npx create-next-app@latest auth0-nextjs --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --yes
Open the project
cd auth0-nextjs
2

Install the Auth0 Next.js SDK

npm install @auth0/nextjs-auth0
3

Create project files

Create all necessary directories and files for Auth0 integration:
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

Setup your Auth0 App

Next up, you need to create a new app on your Auth0 tenant and add the environment variables to your project.You have three options to set up your Auth0 app: use the Quick Setup tool (recommended), run a CLI command, or configure manually via the Dashboard:
5

Create the Auth0 configuration

Add the Auth0 client code to src/lib/auth0.ts:
src/lib/auth0.ts
import { Auth0Client } from '@auth0/nextjs-auth0/server';

export const auth0 = new Auth0Client();
6

Add Proxy

Add the proxy code to 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: [
    /*
     * Match all request paths except for the ones starting with:
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico, sitemap.xml, robots.txt (metadata files)
     */
    "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
  ],
};
Since we’re using a src/ directory, the proxy.ts file is created inside src/. If you’re not using a src/ directory, create it in the project root instead.
This proxy automatically mounts the following authentication routes:
  • /auth/login - Login route
  • /auth/logout - Logout route
  • /auth/callback - Callback route
  • /auth/profile - User profile route
  • /auth/access-token - Access token route
  • /auth/backchannel-logout - Backchannel logout route
7

Create Login, Logout and Profile Components

Add the component code to the files created in Step 3:
8

Update your main page

Replace src/app/page.tsx with:
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-[#060812] flex items-center justify-center px-6 py-12 relative overflow-hidden">
      <div className="absolute top-0 left-1/2 -translate-x-1/2 w-[600px] md:w-[900px] h-[300px] md:h-[450px] bg-blue-600/20 rounded-full blur-3xl pointer-events-none" />
      <div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-[400px] md:w-[600px] h-[200px] md:h-[300px] bg-violet-600/15 rounded-full blur-3xl pointer-events-none" />

      <div className="relative w-full max-w-sm md:max-w-md">
        <div className="bg-white/[0.04] backdrop-blur-2xl border border-white/[0.08] rounded-3xl shadow-2xl shadow-black/60 overflow-hidden">
          <div className="h-px bg-gradient-to-r from-transparent via-blue-500/60 to-transparent" />

          <div className="px-8 md:px-10 pt-9 md:pt-10 pb-9 md:pb-10 flex flex-col items-center gap-6 md:gap-7">
            <img
              src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png"
              alt="Auth0"
              className="h-6 md:h-7"
            />

            <div className="text-center">
              <h1 className="text-xl md:text-2xl font-semibold text-white tracking-[-0.02em]">
                Next.js + Auth0
              </h1>
              <p className="text-slate-500 text-sm md:text-[15px] mt-1.5">
                Secure, simple authentication
              </p>
            </div>

            <div className="w-full h-px bg-white/[0.06]" />

            {user ? (
              <div className="flex flex-col items-center gap-4 w-full">
                <Profile />
                <LogoutButton />
              </div>
            ) : (
              <div className="flex flex-col items-center gap-5 w-full">
                <p className="text-slate-400 text-sm md:text-[15px] text-center leading-relaxed tracking-[-0.01em]">
                  Sign in to access your account and protected content.
                </p>
                <LoginButton />
              </div>
            )}
          </div>
        </div>
      </div>
    </main>
  );
}
9

Update layout with Auth0Provider

Update src/app/layout.tsx to load the Inter font and wrap your app with 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>
  );
}
In v4, the Auth0Provider is optional. You only need it if you want to pass an initial user during server rendering to be available to the useUser() hook.
10

Configure Tailwind CSS

Replace the contents of src/app/globals.css with:
src/app/globals.css
@import "tailwindcss";
11

Run your app

npm run dev
Your app will be available at http://localhost:3000. The Auth0 SDK v4 automatically mounts authentication routes at /auth/* (not /api/auth/* like in v3).If port 3000 is in use, run: npm run dev -- --port 3001 and update your Auth0 app’s callback URLs to http://localhost:3001
CheckpointYou should now have a fully functional Auth0 login page running on your localhost

Troubleshooting

If you see a JWEDecryptionFailed: decryption operation failed error, this is caused by either an invalid AUTH0_SECRET or an old session cookie encrypted with a different secret.Solution:
  1. Generate a new secret using:
openssl rand -hex 32
  1. Update your .env.local file:
AUTH0_SECRET=<your-new-64-character-hex-string>
  1. Clear your browser cookies for localhost:3000:
    • Chrome/Edge: Press F12 → Application tab → Cookies → Delete all cookies for localhost
    • Firefox: Press F12 → Storage tab → Cookies → Delete all cookies for localhost
    • Safari: Develop menu → Show Web Inspector → Storage tab → Cookies → Delete all
  2. Restart your dev server:
npm run dev
The secret must be exactly 32 bytes (64 hexadecimal characters). The error occurs when the app tries to decrypt an existing session cookie that was encrypted with a different secret.
If clicking login takes you to a 404 page, check these common issues:
  1. Proxy location: Ensure src/proxy.ts exists in the correct location
  2. Proxy code: Verify the proxy matches the code in Step 6
  3. Restart server: After creating the proxy file, restart the dev server
  4. Check imports: Make sure import { auth0 } from "./lib/auth0" path is correct
If you see “Cannot find module ’@/components/LoginButton’” or similar errors:
  1. Verify files exist: Check that all files from Step 3 were created
  2. Check paths: Ensure components are in src/components/ directory
  3. Restart TypeScript: Press Cmd+Shift+P (Mac) or Ctrl+Shift+P (Windows) and run “TypeScript: Restart TS Server”
  4. Verify imports: Make sure you’re using @/components/* (not ~/components/*)

Advanced Usage

This quickstart uses Auth0 Next.js SDK v4, which has significant changes from v3:
  • No dynamic route handlers needed - Authentication routes are auto-mounted by the proxy
  • Simplified client setup - new Auth0Client() reads environment variables automatically
  • New route paths - Routes are at /auth/* instead of /api/auth/*
  • Required proxy - All authentication functionality goes through proxy.ts
  • Use <a> tags - Navigation must use <a href="/auth/login"> instead of buttons with onClick

Authentication Routes

The SDK automatically mounts these routes via the proxy:
RoutePurpose
/auth/loginInitiate login
/auth/logoutLogout user
/auth/callbackHandle Auth0 callback
/auth/profileGet user profile
/auth/access-tokenGet access token
/auth/backchannel-logoutHandle backchannel logout
If you’re experiencing 404 errors on these routes, ensure that:
  1. The proxy.ts file is in the correct location (project root, or inside src/ if using a src/ directory)
  2. The proxy is properly configured with the matcher pattern shown in Step 6
  3. The development server was restarted after creating the proxy file
The Auth0 Next.js SDK v4 supports both App Router and Pages Router patterns. Here are some common server-side patterns:
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>
  );
}
For client-side authentication state, use the useUser hook:
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>
  );
}
For API route protection, use the withApiAuthRequired method:
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
  });
});