Documentation Index
Fetch the complete documentation index at: https://mintlify.com/auth0/nextjs-auth0/llms.txt
Use this file to discover all available pages before exploring further.
Protect your API routes to ensure only authenticated users can access them. The SDK provides the withApiAuthRequired helper for both App Router and Pages Router.
App Router
Protect App Router route handlers by wrapping them with withApiAuthRequired.
Create your route handler
Create a route handler file in your app directory:app/api/protected/route.ts
import { NextResponse } from "next/server";
import { auth0 } from "@/lib/auth0";
export const GET = auth0.withApiAuthRequired(async function GET(req) {
const { user } = await auth0.getSession(req);
return NextResponse.json({
message: "This is a protected API route",
userId: user.sub
});
});
Test the route
Test your protected route by making a request:curl http://localhost:3000/api/protected
Without authentication, you’ll receive a 401 Unauthorized response.
Multiple HTTP Methods
Protect multiple HTTP methods in the same route file:
import { NextRequest, NextResponse } from "next/server";
import { auth0 } from "@/lib/auth0";
export const GET = auth0.withApiAuthRequired(async function GET() {
const { user } = await auth0.getSession();
return NextResponse.json({
data: "User data",
userId: user.sub
});
});
export const POST = auth0.withApiAuthRequired(async function POST(
req: NextRequest
) {
const { user } = await auth0.getSession(req);
const body = await req.json();
// Process the data
console.log(`User ${user.sub} posted:`, body);
return NextResponse.json({ success: true });
});
export const DELETE = auth0.withApiAuthRequired(async function DELETE(
req: NextRequest
) {
const { user } = await auth0.getSession(req);
// Delete logic here
return NextResponse.json({ deleted: true });
});
Dynamic Routes
Access route parameters in protected API routes:
app/api/posts/[id]/route.ts
import { NextRequest, NextResponse } from "next/server";
import { auth0 } from "@/lib/auth0";
export const GET = auth0.withApiAuthRequired(
async function GET(req: NextRequest, { params }) {
const { user } = await auth0.getSession(req);
const { id } = await params;
return NextResponse.json({
postId: id,
userId: user.sub,
message: `Fetching post ${id} for user ${user.name}`
});
}
);
Pages Router
Protect Pages Router API routes by wrapping the handler function with withApiAuthRequired.
Create your API route
Create an API route file in your pages/api directory:import type { NextApiRequest, NextApiResponse } from "next";
import { auth0 } from "@/lib/auth0";
export default auth0.withApiAuthRequired(
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { user } = await auth0.getSession(req);
res.status(200).json({
message: "This is a protected API route",
userId: user.sub
});
}
);
Test the route
Make a request to test your protected route:curl http://localhost:3000/api/protected
Without a valid session cookie, you’ll receive a 401 Unauthorized response.
Handling Different HTTP Methods
Handle multiple HTTP methods in a single Pages Router API route:
import type { NextApiRequest, NextApiResponse } from "next";
import { auth0 } from "@/lib/auth0";
type ResponseData = {
message?: string;
error?: string;
};
export default auth0.withApiAuthRequired(
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
const { user } = await auth0.getSession(req);
switch (req.method) {
case "GET":
return res.status(200).json({
message: `Hello ${user.name}`
});
case "POST":
// Process POST data
const body = req.body;
return res.status(201).json({
message: "Data created"
});
case "DELETE":
// Delete logic
return res.status(200).json({
message: "Data deleted"
});
default:
res.setHeader("Allow", ["GET", "POST", "DELETE"]);
return res.status(405).json({
error: `Method ${req.method} Not Allowed`
});
}
}
);
Dynamic API Routes
Access dynamic route parameters in Pages Router:
import type { NextApiRequest, NextApiResponse } from "next";
import { auth0 } from "@/lib/auth0";
export default auth0.withApiAuthRequired(
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { user } = await auth0.getSession(req);
const { id } = req.query;
res.status(200).json({
postId: id,
userId: user.sub,
message: `Fetching post ${id} for user ${user.name}`
});
}
);
Calling Protected API Routes
Once your API routes are protected, you can call them from your frontend with the session cookie.
Call protected API routes from client components:"use client";
import { withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import useSWR from "swr";
const fetcher = async (uri: string) => {
const response = await fetch(uri);
if (!response.ok) throw new Error("Failed to fetch");
return response.json();
};
function Products() {
const { data, error } = useSWR("/api/protected", fetcher);
if (error) return <div>Error: {error.message}</div>;
if (!data) return <div>Loading...</div>;
return <div>{data.message}</div>;
}
export default withPageAuthRequired(Products);
Call protected API routes from Pages Router pages:import { withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import useSWR from "swr";
const fetcher = async (uri: string) => {
const response = await fetch(uri);
if (!response.ok) throw new Error("Failed to fetch");
return response.json();
};
function Products() {
const { data, error } = useSWR("/api/protected", fetcher);
if (error) return <div>Error: {error.message}</div>;
if (!data) return <div>Loading...</div>;
return <div>{data.message}</div>;
}
export default withPageAuthRequired(Products);
Error Responses
When a request to a protected API route fails authentication, the SDK returns a 401 Unauthorized response:
{
"error": "not_authenticated",
"description": "The user does not have an active session or is not authenticated"
}
Access the authenticated user’s information within your protected API route:
import { auth0 } from "@/lib/auth0";
export const GET = auth0.withApiAuthRequired(async function GET(req) {
const { user } = await auth0.getSession(req);
// User object contains:
// - user.sub: unique user ID
// - user.name: user's name
// - user.email: user's email
// - user.picture: user's profile picture URL
// ... and other profile fields
return Response.json({
userId: user.sub,
name: user.name,
email: user.email
});
});
Calling External APIs
To call external APIs from your protected routes, use getAccessToken to obtain an access token:
app/api/external-data/route.ts
import { NextResponse } from "next/server";
import { auth0 } from "@/lib/auth0";
export const GET = auth0.withApiAuthRequired(async function GET() {
try {
// Get access token for external API
const { token } = await auth0.getAccessToken();
// Call external API with the token
const response = await fetch("https://api.example.com/data", {
headers: {
Authorization: `Bearer ${token}`
}
});
const data = await response.json();
return NextResponse.json(data);
} catch (error) {
return NextResponse.json(
{ error: "Failed to fetch data" },
{ status: 500 }
);
}
});
Best Practices
- Always validate user permissions beyond just authentication when accessing sensitive resources
- Use TypeScript for type safety with request/response objects
- Handle errors gracefully and return appropriate HTTP status codes
- Never expose sensitive data in error messages
- Use HTTPS in production to protect session cookies from interception
Troubleshooting
401 Errors with Valid Session
If you’re getting 401 errors despite being logged in:
- Check session cookie: Ensure the session cookie is being sent with requests
- Verify domain: If using subdomains, ensure cookie domain is set correctly
- Check SameSite: Set
sameSite: "lax" for cross-origin requests
Session not available in App Router
If getSession() returns null in App Router:
- Ensure you’re passing the
req parameter: auth0.getSession(req)
- Verify middleware is running on the request path
- Check that cookies are being sent from the client
CORS Issues
For API routes called from different origins:
import { NextResponse } from "next/server";
export const GET = auth0.withApiAuthRequired(async function GET(req) {
const response = NextResponse.json({ data: "Protected data" });
// Add CORS headers
response.headers.set("Access-Control-Allow-Origin", "https://example.com");
response.headers.set("Access-Control-Allow-Credentials", "true");
return response;
});