Copy-pasted getSession() + inline if (!session) return 401 blocks in every route handler are a maintenance hazard. When the session-check logic needs to change — adding a new auth provider, switching libraries, or tightening the token validation — every handler must be found and updated. One missed handler leaves an unguarded route. OWASP A01 (Broken Access Control) and CWE-284 explicitly cover the failure mode where access enforcement is inconsistent across handlers. Centralized middleware makes the enforcement surface one file, one audit.
Low because the immediate security impact is maintenance risk and inconsistency rather than a direct bypass, but the long-term risk of drift is meaningful.
Wrap route handlers with a withAuth higher-order function so authentication logic lives in one place. Route handlers receive the verified session as a parameter and never call getSession directly.
// lib/auth/protected-handler.ts
import { auth } from '@/lib/auth';
import { NextRequest, NextResponse } from 'next/server';
type Handler = (req: NextRequest, session: Session) => Promise<NextResponse>;
export function withAuth(handler: Handler) {
return async (req: NextRequest) => {
const session = await auth();
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
return handler(req, session);
};
}
For Next.js App Router, also add middleware.ts to protect entire route groups at the edge before requests reach handlers.
ID: saas-authorization.access-control.authorization-centralized
Severity: low
What to look for: Count all relevant instances and enumerate each. Search for repeated patterns across route handler files — are session fetches and role checks copy-pasted into each handler, or do handlers call a shared utility? Look for a requireAuth, withAuth, protectedRoute, or equivalent function used across API routes. Check middleware.ts for route protection at the edge level.
Pass criteria: The majority of protected routes (>80%) use a shared middleware, wrapper function, or hook for authorization rather than implementing their own inline session-and-role checks. A central middleware file exists that protects route groups. A partial or placeholder implementation does not count as pass.
Fail criteria: Multiple API route files each implement their own isolated authentication and authorization logic with repeated getSession() calls and inline if (!session) return 401 patterns, with no shared abstraction.
Skip (N/A) when: The project has fewer than 3 API routes (centralization has no meaningful benefit at this scale).
Detail on fail: "Authorization logic is repetitive across route handlers. Found inline auth checks in N route files with no shared middleware or wrapper." (Replace N with the actual count.)
Remediation: Create a higher-order function or Next.js Middleware that handles the authentication and authorization check once. Route handlers should receive the verified session as a parameter rather than fetching and validating it themselves.
// lib/auth/protected-handler.ts
import { auth } from '@/lib/auth';
import { NextRequest, NextResponse } from 'next/server';
type Handler = (req: NextRequest, session: Session) => Promise<NextResponse>;
export function withAuth(handler: Handler) {
return async (req: NextRequest) => {
const session = await auth();
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
return handler(req, session);
};
}
For Next.js, also consider using middleware.ts to protect entire route groups at the edge before requests reach route handlers.