Back to Security Academy
API SECURITYINTERMEDIATE7 min readCWE-862ZX-SEC-API-001ZX-AUTH-001

Securing Next.js Server Actions Against Missing Authorization

Server Actions are publicly accessible POST endpoints by default. Here is how to prevent unauthenticated execution and broken access control.

Why AI coding tools introduce this vulnerability

AI models treat Server Actions as simple "server-side helper functions" and often omit session authentication checks, assuming that because the function is inside a client component, only authenticated users can invoke it. In reality, any attacker can curl the Next.js action ID directly.

Technical Vulnerability Analysis

Next.js compiles every "use server" action into an exposed HTTP POST endpoint identifiable by its internal Next-Action hash. Without explicit session verification at the very beginning of the function, unauthenticated attackers can delete accounts, modify billing records, or export proprietary data.

VULNERABLE IMPLEMENTATION (DO NOT USE)
// app/actions/user.ts (VULNERABLE)
'use server';

export async function deleteUserAccount(targetUserId: string) {
  // AI forgot to check WHO is making this request!
  // Anyone with the action ID can delete any user!
  await db.query('DELETE FROM users WHERE id = $1', [targetUserId]);
}
No authentication check and no verification that targetUserId matches the authenticated requester.

Remediation Playbook & Code Fixes

SECURE HARDENED PATCH (src/actions/user.ts)
'use server';

import { getServerSession } from '@/core/auth/session-service';

export async function deleteUserAccount(targetUserId: string) {
  // 1. Mandatory server-side authentication
  const session = await getServerSession();
  if (!session) {
    throw new Error('Unauthorized');
  }

  // 2. Strict authorization & tenant boundary check
  if (session.user.id !== targetUserId && session.user.role !== 'SUPER_ADMIN') {
    throw new Error('Forbidden: You can only delete your own account');
  }

  await db.query('DELETE FROM users WHERE id = $1', [targetUserId]);
  return { success: true };
}

Why this fix works: Always verify session and assert user permissions within the server action body.

Local CLI Verification Commands

Verify unauthenticated invocation of server action is rejected.
curl -s -X POST https://example.com -H 'Next-Action: abc123def' -d '["user-id-to-delete"]'
Expected result: 401 Unauthorized or {"error":"Unauthorized"}

Pre-Deployment Self-Audit Checklist

Knowledge Check: Verify Your Understanding

What is the primary architectural cause of securing next.js server actions against missing authorization?

Test your website for ZX-SEC-API-001

Run our 14 deterministic engines to inspect your live deployment.

Launch scan for this flaw