nextjs-server-action-safety
Defend against client/server boundary confusion in Next.js Server Actions.
Skill by MILTONADINA
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/miltonadina-nextjs-server-action-safety
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: frontend, backend, security
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/nextjs-server-action-safety
What's inside
- Skill name
nextjs-server-action-safety- Description
- Defend against client/server boundary confusion in Next.js Server Actions. Patterns covered include authn drift in token-bound mutations, input validation at the action boundary, and mutation idempotency under retry. Triggers on Server Action authoring (functions with `use server` directive) and Next.js form-action references. OWASP ASI02 (Tool Misuse) plus ASI03 (Identity & Privilege Abuse -- authn drift) defense.
- License
- MIT
- Instructions
- 280 lines in SKILL.md · view on GitHub
Agent Skills spec issues
- name: "nextjs-server-action-safety" does not match folder "server-action-safety"
Files (1)
- SKILL.md
SKILL.md (first lines)
# Next.js Server Action Safety
> Defends against **OWASP ASI02 (Tool Misuse)** -- a Server Action invoked
> from a wrong client context (logged-out tab, different user's session, an
> attacker's CSRF-shaped payload) re-invokes the server-side mutation with
> the agent's logged-in privileges -- AND **OWASP ASI03 (Identity & Privilege
> Abuse)** -- authn drift where a token-bound mutation runs with stale or
> elevated identity because the action implicitly trusts client-passed
> context instead of re-deriving identity from the server-side session.
**Tradeoff:** Server Actions are a powerful ergonomic for mutations -- they
collapse the form-POST + route-handler + revalidation triad into one
function declaration. The cost: the function is exposed at a public URL
(Next.js compiles each action to a callable endpoint), so it must be
treated like any other public mutation entry point. Without the patterns
below, the convenience leaks into the security model.
---
## Why this matters
Server Actions look like local function calls (`await myAction(formData)`)
but they're actually HTTP POST endpoints generated at build time. Any
authenticated tab can invoke them. Any unauthenticated client can probe
the endpoint URL. Any code in the same Next.js app can import and call
them. The "server-only" annotation (`'use server'`) marks the boundary --
it does NOT add authentication, authorization, validation, or rate
limiting.
A handler that doesn't explicitly re-verify identity, validate input, and
guard against replay is structurally identical to an unauthenticated POST
endpoint. The familiar function-call surface hides this fact from authors
who haven't shipped Next.js mutations in production.
---
## Pattern 1 -- Re-derive identity from server-side session inside every action
Inside the action, ALWAYS read the session from a server-side source
(cookie-backed session store, `auth()` helper, JWT verified against your
own signing secret). Never trust an `actorId` / `userId` / `tenantId`
passed as a form field or function argument -- it's attacker-controllable.
```ts
'use server';
import { auth } from '@/lib/auth';
import { db } from '@/lib/db';
import { z } from 'zod';
const UpdateProfileInput = z.object({
displayName: z.string().min(1).max(120),
bio: z.string().max(2000).optional(),
});
export async function updateProfile(formData: FormData) {
// 1. Re-derive identity. session.userId is the server-side truth.
const session = await auth();
if (!session?.userId) {
throw new Error('unauthenticated');
}
// 2. Validate input at the boundary -- the function signature accepts
// FormData (any shape); Zod is the runtime contract.
const parsed = UpdateProfileInput.safeParse({
displayName: formData.get('displayName'),
bio: formData.get('bio'),
});
if (!parsed.success) {
throw new Error('invalid input');
}
// 3. Scope the mutation to the session-derived identity, not any
// client-passed identifier. db.user.update WHERE id = session.userId.
await db.user.update({
where: { id: session.userId },
data: parsed.data,
});
}
```
A defender reviewing this action can answer in one minute: "Who is this
mutation acting on?" The answer is "the server-derived session user, not
whoever the client says they are."
---
## Pattern 2 -- Validate input with a runtime schema at the action boundary
TypeScript types are compile-time only. The `FormData` argument is the
real runtime contract -- it's whatever the client posted, which may be
arbitrary strings, arrays, or absent fields.
Use Zod / Valibot / a similar runtime validator. Validate at the first
line of the action body. Reject invalid input with a generic error
message (don't echo the invalid value back -- it's potentially attacker-
controlled and may be reflected somewhere).
The validator's schema is also documentation: a defender can read it and
know exactly which fields the action accepts and what the bounds are.
---
## Pattern 3 -- Idempotency for mutations that the client may retry
Server Actions are invoked from forms and from optimistic-UI patterns,
both of which can retry on transient failure (network blip, slow render,
user double-clicks Submit). A mutation that books a flight, charges a
card, or sends a message has to handle the retry without producing two
side effects.
The universal pattern: an idempotency key passed through the action and
checked against a dedupe store. The Stripe-style approach works here too:
```ts
'use server';
export async function chargeOrder(formData: FormData) {
const session = await auth();From MILTONADINA/DevOPs, MIT.
More from MILTONADINA/DevOPs
- 0.565score
cost-attribution
MILTONADINA
Track per-client and per-feature LLM and cloud spend with cryptographic audit trail.
SkillAFreshness: AClaude Code +9 - 0.565score
proof-of-work
MILTONADINA
Produce structured proof artifacts for every completed claim.
SkillAFreshness: AClaude Code +9 - 0.565score
plan-decomposition
MILTONADINA
Break an approved spec into atomic, ordered, verifiable tasks before any code is written.
SkillAFreshness: AClaude Code +9 - 0.565score
multi-tool-failover
MILTONADINA
Manage session continuity when switching between Claude Code, Codex CLI, Cursor, Antigravity, Kiro, or local LLMs (OpenCode, Aider).
SkillAFreshness: AClaude Code +9 - 0.565score
goal-loop
MILTONADINA
Execute the spec-tests-implement-verify cycle for one atomic task at a time.
SkillAFreshness: AClaude Code +9 - 0.565score
baton-handoff
MILTONADINA
Write a complete handoff document at session end so the next agent (Claude Code, Codex, Cursor, Antigravity, Kiro, or local LLM) resumes exactly where this…
SkillAFreshness: AClaude Code +9 - 0.565score
observability-instrument
MILTONADINA
Add OpenTelemetry spans with user_id and tenant_id in baggage to any new code path that handles requests, runs agents, or performs billable operations.
SkillAFreshness: AClaude Code +9 - 0.565score
ears-spec-writing
MILTONADINA
Write requirement specifications in EARS (Easy Approach to Requirements Syntax) notation.
SkillAFreshness: AClaude Code +9 - 0.565score
openapi-first
MILTONADINA
Generate API code, types, validators, mock servers, and client SDKs from a single OpenAPI 3.1 specification.
SkillAFreshness: AClaude Code +9 - 0.561score
session-summary
MILTONADINA
Generate the end-of-session human-reviewable report.
SkillAFreshness: AClaude Code +9 - 0.561score
coppa-audit
MILTONADINA
Audit code, data flows, and UI for COPPA (Children's Online Privacy Protection Act) compliance.
SkillAFreshness: AClaude Code +9 - 0.560score
supabase-rpc-functions
MILTONADINA
Supabase RPC function security patterns -- SECURITY DEFINER with `SET search_path` discipline, `REVOKE EXECUTE FROM public` for least-privilege exposure…
SkillAFreshness: AClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 12
- Last commit (this folder)
- Sep 25, 2026 (1 month ago)
- Last release
- Sep 23, 2026 (1 month ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/MILTONADINA/DevOPs
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from MILTONADINA
- 0.565score
baton-handoff
MILTONADINA
Write a complete handoff document at session end so the next agent (Claude Code, Codex, Cursor, Antigravity, Kiro, or local LLM) resumes exactly where this…
SkillAFreshness: AClaude Code +9 - 0.565score
observability-instrument
MILTONADINA
Add OpenTelemetry spans with user_id and tenant_id in baggage to any new code path that handles requests, runs agents, or performs billable operations.
SkillAFreshness: AClaude Code +9 - 0.565score
openapi-first
MILTONADINA
Generate API code, types, validators, mock servers, and client SDKs from a single OpenAPI 3.1 specification.
SkillAFreshness: AClaude Code +9 - 0.565score
ears-spec-writing
MILTONADINA
Write requirement specifications in EARS (Easy Approach to Requirements Syntax) notation.
SkillAFreshness: AClaude Code +9 - 0.565score
cost-attribution
MILTONADINA
Track per-client and per-feature LLM and cloud spend with cryptographic audit trail.
SkillAFreshness: AClaude Code +9 - 0.565score
goal-loop
MILTONADINA
Execute the spec-tests-implement-verify cycle for one atomic task at a time.
SkillAFreshness: AClaude Code +9
Cite this page
nextjs-server-action-safety skill. Agent Mart. https://agentmart.dev/skills/miltonadina-nextjs-server-action-safety (last updated 2026-10-24)