Add authentication to Next.js applications using @auth0/nextjs-auth0. Supports both App Router and Pages Router.
auth0-quickstart skill firstauth0-react for Vite/CRA SPAsauth0-react-native for iOS/Androidbash
npm install @auth0/nextjs-auth0
For automated setup with Auth0 CLI, see Setup Guide for complete scripts.
For manual setup:
Create .env.local:
bash
AUTH0_SECRET=<generate-a-32-character-secret>
APP_BASE_URL=http://localhost:3000
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your-client-id
AUTH0_CLIENT_SECRET=your-client-secret
Generate secret: openssl rand -hex 32
Important: Add .env.local to .gitignore
Detect project structure first: Check whether the project uses a src/ directory (i.e. src/app/ or src/pages/ exists). This determines where to place files:
src/: src/lib/auth0.ts, src/middleware.ts (or src/proxy.ts for Next.js 16)src/: lib/auth0.ts, middleware.ts (or proxy.ts for Next.js 16)Create lib/auth0.ts (or src/lib/auth0.ts if using the src/ convention):
typescript
import { Auth0Client } from '@auth0/nextjs-auth0/server';
export const auth0 = new Auth0Client({
domain: process.env.AUTH0_DOMAIN!,
clientId: process.env.AUTH0_CLIENT_ID!,
clientSecret: process.env.AUTH0_CLIENT_SECRET!,
secret: process.env.AUTH0_SECRET!,
appBaseUrl: process.env.APP_BASE_URL!,
});
Middleware Configuration (Next.js 15 vs 16):
Next.js 15 - Create middleware.ts (at project root, or src/middleware.ts if using src/):
typescript
import { NextRequest } from 'next/server';
import { auth0 } from '@/lib/auth0';
export async function middleware(request: NextRequest) {
return await auth0.middleware(request);
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
};
Next.js 16 - You have two options:
Option 1: Use middleware.ts (same as Next.js 15, same src/ placement rules):
typescript
import { NextRequest } from 'next/server';
import { auth0 } from '@/lib/auth0';
export async function middleware(request: NextRequest) {
return await auth0.middleware(request);
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
};
Option 2: Use proxy.ts (at project root, or src/proxy.ts if using src/):
typescript
import { NextRequest } from 'next/server';
import { auth0 } from '@/lib/auth0';
export async function proxy(request: NextRequest) {
return await auth0.middleware(request);
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
};
This automatically creates endpoints:
/auth/login - Login/auth/logout - Logout/auth/callback - OAuth callback/auth/profile - User profileNote: In v4, wrapping with <Auth0Provider> is optional. Only needed if you want to pass an initial user during server rendering to useUser().
App Router - Optionally wrap app in app/layout.tsx:
typescript
import { Auth0Provider } from '@auth0/nextjs-auth0/client';
import { auth0 } from '@/lib/auth0';
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const session = await auth0.getSession();
return (
<html>
<body>
<Auth0Provider user={session?.user}>{children}</Auth0Provider>
</body>
</html>
);
}
Pages Router - Optionally wrap app in pages/_app.tsx:
typescript
import { Auth0Provider } from '@auth0/nextjs-auth0/client';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<Auth0Provider user={pageProps.user}>
<Component {...pageProps} />
</Auth0Provider>
);
}
Client Component (works in both routers):
typescript
'use client'; // Only needed for App Router
import { useUser } from '@auth0/nextjs-auth0/client';
export default function Profile() {
const { user, isLoading } = useUser();
if (isLoading) return <div>Loading...</div>;
if (user) {
return (
<div>
<img src={user.picture} alt={user.name} />
<h2>Welcome, {user.name}!</h2>
<a href="/auth/logout">Logout</a>
</div>
);
}
return <a href="/auth/login">Login</a>;
}
Start your dev server:
bash
npm run dev
Visit http://localhost:3000 and test the login flow.
MistakeFixUsing v3 environment variablesv4 uses APP_BASE_URL and AUTH0_DOMAIN (not AUTH0_BASE_URL or AUTH0_ISSUER_BASE_URL)Forgot to add callback URL in Auth0 DashboardAdd /auth/callback to Allowed Callback URLs (e.g., http://localhost:3000/auth/callback)Missing middleware configurationv4 requires middleware to mount auth routes - create middleware.ts (Next.js 15+16) or proxy.ts (Next.js 16 only) with auth0.middleware()Wrong route pathsv4 uses /auth/login not /api/auth/login - routes drop the /api prefixMissing or weak AUTH0_SECRETGenerate secure secret with openssl rand -hex 32 and store in .env.localUsing .env instead of .env.localNext.js requires .env.local for local secrets, and .env.local should be in .gitignoreApp created as SPA type in Auth0Must be Regular Web Application type for Next.jsUsing removed v3 helpersv4 removed withPageAuthRequired and withApiAuthRequired - use getSession() insteadUsing useUser in Server ComponentuseUser is client-only, use auth0.getSession() for Server ComponentsAUTH0_DOMAIN includes https://v4 AUTH0_DOMAIN should be just the domain (e.g., example.auth0.com), no scheme
auth0-quickstart - Basic Auth0 setupauth0-migration - Migrate from another auth providerauth0-mfa - Add Multi-Factor Authenticationauth0-cli - Manage Auth0 resources from the terminalV4 Setup:
src/ convention: check if src/app/ or src/pages/ exists — place all files inside src/ if solib/auth0.ts (or src/lib/auth0.ts) with Auth0Client instancemiddleware.ts (or src/middleware.ts) with middleware() functionmiddleware.ts with middleware() OR proxy.ts with proxy() function (same src/ rules)<Auth0Provider> for SSR userClient-Side Hooks:
useUser() - Get user in client componentsuser - User profile objectisLoading - Loading stateServer-Side Methods:
auth0.getSession() - Get session in Server Components/API routes/middlewareauth0.getAccessToken() - Get access token for calling APIsCommon Use Cases:
/auth/login and /auth/logout paths (see Step 5)