Agent skill
securing-data-access-layer
Teach Data Access Layer pattern to prevent CVE-2025-29927 middleware authentication bypass. Use when implementing authentication, authorization, protecting routes, or working with server actions that need auth.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/securing-data-access-layer
SKILL.md
Data Access Layer Pattern for Next.js 16 Authentication Security
Critical Security Issue: CVE-2025-29927
Next.js 16 has a critical authentication bypass vulnerability in middleware. Middleware NextResponse.redirect() and NextResponse.rewrite() DO NOT terminate execution, allowing unauthorized access to protected resources.
The Problem
export function middleware(request: NextRequest) {
const session = request.cookies.get('session');
if (!session) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
This middleware appears to protect routes, but code after the return statement still executes. Attackers can bypass authentication by manipulating requests.
Why This Matters
- Middleware-only authentication is completely broken in Next.js 16
- Protected routes, server actions, and API routes are all vulnerable
- Data breaches, unauthorized access, and privilege escalation are possible
The Solution: Data Access Layer (DAL)
Implement a multi-layer security strategy with authentication verification at every access point.
Core Pattern: verifySession()
For comprehensive input validation patterns to use alongside authentication, use the sanitizing-user-inputs skill from the typescript plugin.
Create a centralized Data Access Layer that verifies authentication before ANY data access:
import 'server-only';
import { cookies } from 'next/headers';
import { decrypt } from '@/lib/session';
import { cache } from 'react';
export const verifySession = cache(async () => {
const cookie = (await cookies()).get('session')?.value;
const session = await decrypt(cookie);
if (!session?.userId) {
throw new Error('Unauthorized');
}
return { isAuth: true, userId: session.userId };
});
Key features:
- Uses
cache()for request-level memoization (single verification per request) - Throws error if unauthorized (fails fast)
- Returns typed session data for use in application logic
- Server-only code that cannot leak to client
Three-Layer Security Architecture
- Route Protection - Basic UX (redirect unauthorized users)
- Data Access Layer - Core security (verify before data access)
- Server Actions - Action-level verification (verify before mutations)
Layer 1: Route Protection (UX Only)
export default async function DashboardLayout({ children }) {
const session = await verifySession();
if (!session.isAuth) {
redirect('/login');
}
return <>{children}</>;
}
Verify session in layouts and pages to redirect unauthorized users. This is UX only, not security.
Layer 2: Data Access Layer (Security)
export async function getUser() {
const session = await verifySession();
const data = await db.query.users.findMany({
where: eq(users.id, session.userId),
});
return data;
}
Always verify session before database queries. This is your actual security boundary.
For type-safe database access patterns, use the ensuring-query-type-safety skill from prisma-6 to prevent type errors and runtime failures in your data access functions.
Layer 3: Server Actions (Mutation Security)
'use server';
export async function updateProfile(formData: FormData) {
const session = await verifySession();
const name = formData.get('name');
await db.update(users).set({ name }).where(eq(users.id, session.userId));
revalidatePath('/profile');
}
Verify session at the start of every server action that modifies data.
Implementation Checklist
When working with authenticated features:
- Create
lib/dal.tswithverifySession()function - Use
verifySession()in ALL data fetching functions - Use
verifySession()in ALL server actions - Add route protection to layouts/pages for UX (optional but recommended)
- Never rely on middleware alone for authentication
- Import 'server-only' in DAL to prevent client leaks
- Use React
cache()for request-level memoization
Common Patterns
Authorization (Role-Based Access)
export async function verifyAdmin() {
const session = await verifySession();
const user = await db.query.users.findFirst({
where: eq(users.id, session.userId),
});
if (user?.role !== 'admin') {
throw new Error('Forbidden');
}
return { userId: session.userId, role: user.role };
}
Resource Ownership
export async function getPost(postId: string) {
const session = await verifySession();
const post = await db.query.posts.findFirst({
where: eq(posts.id, postId),
});
if (post.authorId !== session.userId) {
throw new Error('Forbidden');
}
return post;
}
Multi-Step Atomic Operations
If setting up PrismaClient with singleton pattern in Next.js, use the creating-client-singletons skill from prisma-6 for proper instantiation preventing connection pool exhaustion.
If implementing authenticated operations requiring atomicity (e.g., creating a post with tags, transferring ownership), use the using-interactive-transactions skill from prisma-6 for database-specific transaction patterns.
Public + Private Data
export async function getProfile(username: string) {
const session = await verifySession().catch(() => null);
const profile = await db.query.users.findFirst({
where: eq(users.username, username),
columns: {
username: true,
bio: true,
email: session ? true : false,
},
});
return profile;
}
Key Takeaways
- CVE-2025-29927 makes middleware authentication unsafe - redirects don't stop execution
- Data Access Layer is mandatory - verify session before every data access
- Multi-layer security - route protection (UX) + DAL (security) + server actions (mutations)
- verifySession() everywhere - make it a habit to call this first
- Use React cache() - prevents multiple verification calls per request
References
See the references/ directory for:
dal-example.md- Complete working example with full implementationcve-2025-29927.md- Detailed vulnerability analysis and exploitation examples
When to Use This Skill
Apply this pattern when:
- Implementing authentication in Next.js 16 applications
- Protecting routes that require user login
- Creating server actions that modify user data
- Building authorization systems (roles, permissions)
- Fetching user-specific data from databases
- Any time you see middleware being used for authentication
- Reviewing code for security vulnerabilities
This is the most important security pattern in Next.js 16. Use it in every authenticated application.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?