Server Action auth guard — every Server Action needs session check
Server Actions execute server-side but default to no auth. Add session-required guard at the top of every protected action.
Server Action = server-side function callable from client. Without auth check at the top, any caller can invoke it.
What it is
Server Actions are Next.js's mechanism for client-to-server RPC. Just because they run server-side doesn't mean they're authenticated by default.
Vulnerable example
"use server";
export async function transferFunds(toUserId: string, amount: number) {
// No session check — any caller (logged-in or not) can transfer funds
await db.transfer({ from: "???", to: toUserId, amount });
}Fixed example
"use server";
import { auth } from "@/auth";
export async function transferFunds(toUserId: string, amount: number) {
const session = await auth();
if (!session) throw new Error("unauthenticated");
// Plus authorization: confirm the from-account belongs to this session
const fromAccount = await db.accounts.findOne({ userId: session.user.id });
if (!fromAccount) throw new Error("forbidden");
await db.transfer({ from: fromAccount.id, to: toUserId, amount });
}How Securie catches it
apps/web/auth.ts:87Server Action auth guard
AuthAuthz specialist catches Server Actions without session check at PR time. Sandbox-verifies by attempting the action without a session.
"use server";
import { auth } from "@/auth";
export async function transferFunds(toUserId: string, amount: number) {
const session = await auth();
if (!session) throw new Error("unauthenticated");
// Plus authorization: confirm the from-account belongs to this session
const fromAccount = await db.accounts.findOne({ userId: session.user.id });
if (!fromAccount) throw new Error("forbidden");
await db.transfer({ from: fromAccount.id, to: toUserId, amount });
}Checklist
- const session = await auth() at top of every action
- if (!session) throw — fail-closed
- Authorization check after authentication
- Re-MFA for high-risk actions
- Audit log every state change
FAQ
What about read-only actions?
Same pattern — even reads need auth if returning user-specific data.
Related guides
Server Actions are Next.js's RPC mechanism — async functions marked "use server" that run on the server but are called from client components. The convenience hides the risk: every Server Action is an unauthenticated public API endpoint by default. Here is the vulnerable pattern, the fix, and the audit checklist.
Server Actions accept cross-origin POSTs by default. Add origin check or token validation.
NEXT_PUBLIC_-prefixed env vars ship in the client bundle. Server secrets accidentally prefixed = bundled credentials shipped to every visitor. Here's the detection + fix.
Vercel doesn't set security headers by default. HSTS, CSP, X-Frame-Options, Permissions-Policy all need explicit config. Here's the canonical next.config.mjs.