Broken Authentication in Next.js
Next.js authentication breaks when developers rely on client-side checks, misconfigure middleware matchers, leave API routes unprotected, or improperly validate JWTs. The App Router and Server Components add complexity with multiple auth check points that must all be correctly implemented.
Scan Your Next.js AppHow Broken Authentication Manifests in Next.js
Broken auth in Next.js typically appears as: Middleware that checks auth but has an incorrect matcher pattern, leaving some routes unprotected. For example, matching only /dashboard but missing /dashboard/settings or /api/admin endpoints. Client-side auth checks in useEffect that redirect unauthenticated users but still render the page briefly, allowing data to be captured by disabling JavaScript. API routes that do not verify the session independently, trusting that middleware already checked auth. If middleware is bypassed via direct API calls, the route is unprotected. Server Components that fetch data without checking the session, exposing private data in the server-rendered HTML.
Real-World Impact
A Next.js SaaS dashboard used middleware to protect /dashboard routes but forgot to include /api/admin in the matcher. An attacker discovered the unprotected API endpoints and used them to create admin accounts, export all user data, and modify billing configurations without authentication.
Step-by-Step Fix
Validate auth in every API route
Never trust middleware alone. Check the session in each route handler.
// app/api/admin/users/route.ts
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { NextResponse } from 'next/server';
export async function GET() {
const session = await getServerSession(authOptions);
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
if (session.user.role !== 'admin') {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
const users = await getUsers();
return NextResponse.json(users);
}Configure comprehensive middleware matcher
Ensure middleware covers all protected routes including API endpoints.
// middleware.ts
import { withAuth } from 'next-auth/middleware';
export default withAuth({
pages: { signIn: '/login' },
});
export const config = {
matcher: [
'/dashboard/:path*',
'/api/admin/:path*',
'/api/user/:path*',
'/settings/:path*',
],
};Check auth in Server Components
Validate the session in every Server Component that displays private data.
// app/dashboard/page.tsx
import { getServerSession } from 'next-auth';
import { redirect } from 'next/navigation';
import { authOptions } from '@/lib/auth';
export default async function Dashboard() {
const session = await getServerSession(authOptions);
if (!session) redirect('/login');
const data = await getPrivateData(session.user.id);
return <DashboardView data={data} />;
}Secure Server Actions
Always verify auth at the start of every Server Action.
'use server'
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
export async function updateProfile(formData: FormData) {
const session = await getServerSession(authOptions);
if (!session) throw new Error('Unauthorized');
await db.user.update({
where: { id: session.user.id },
data: { name: formData.get('name') as string },
});
}Prevention Best Practices
1. Check authentication in every Server Component, API route, and Server Action - never rely solely on middleware. 2. Use a comprehensive middleware matcher that covers all protected routes. 3. Never check auth only on the client side - always validate server-side. 4. Use next-auth or clerk with proper session validation on each request. 5. Test auth by directly accessing API routes and pages without cookies.
How to Test
1. Access protected pages directly by URL without logging in. 2. Call API routes with curl without session cookies. 3. Check if middleware matcher covers all protected paths. 4. Disable JavaScript and visit protected pages to check server-side rendering. 5. Use Vibe App Scanner to detect broken auth patterns in your Next.js app.
Frequently Asked Questions
Is Next.js middleware enough for authentication?
No. Middleware is a good first line of defense but should not be the only auth check. Always validate the session in API routes, Server Components, and Server Actions independently. Middleware can be bypassed if matchers are misconfigured.
Should I check auth client-side or server-side in Next.js?
Always check server-side. Client-side auth checks (useEffect redirects) can be bypassed by disabling JavaScript or intercepting the initial response. Use getServerSession in Server Components and API routes for reliable auth validation.
How do I protect Server Actions from unauthorized access?
Call getServerSession() at the beginning of every Server Action and throw an error or return early if the session is invalid. Server Actions are HTTP endpoints and can be called directly, so they need independent auth checks.
Related Security Resources
Is Your Next.js App Vulnerable to Broken Authentication?
VAS automatically scans for broken authentication vulnerabilities in Next.js applications and provides step-by-step remediation guidance with code examples.
Scans from $5, results in minutes. Get actionable fixes tailored to your Next.js stack.