Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Apply 62 React and Next.js performance optimization rules from Vercel Engineering
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/server-auth-actions.md
1---2title: Authenticate Server Actions Like API Routes3impact: CRITICAL4impactDescription: prevents unauthorized access to server mutations5tags: server, server-actions, authentication, security, authorization6---78## Authenticate Server Actions Like API Routes910**Impact: CRITICAL (prevents unauthorized access to server mutations)**1112Server Actions (functions with `"use server"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.1314Next.js documentation explicitly states: "Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation."1516**Incorrect (no authentication check):**1718```typescript19'use server'2021export async function deleteUser(userId: string) {22// Anyone can call this! No auth check23await db.user.delete({ where: { id: userId } })24return { success: true }25}26```2728**Correct (authentication inside the action):**2930```typescript31'use server'3233import { verifySession } from '@/lib/auth'34import { unauthorized } from '@/lib/errors'3536export async function deleteUser(userId: string) {37// Always check auth inside the action38const session = await verifySession()3940if (!session) {41throw unauthorized('Must be logged in')42}4344// Check authorization too45if (session.user.role !== 'admin' && session.user.id !== userId) {46throw unauthorized('Cannot delete other users')47}4849await db.user.delete({ where: { id: userId } })50return { success: true }51}52```5354**With input validation:**5556```typescript57'use server'5859import { verifySession } from '@/lib/auth'60import { z } from 'zod'6162const updateProfileSchema = z.object({63userId: z.string().uuid(),64name: z.string().min(1).max(100),65email: z.string().email()66})6768export async function updateProfile(data: unknown) {69// Validate input first70const validated = updateProfileSchema.parse(data)7172// Then authenticate73const session = await verifySession()74if (!session) {75throw new Error('Unauthorized')76}7778// Then authorize79if (session.user.id !== validated.userId) {80throw new Error('Can only update own profile')81}8283// Finally perform the mutation84await db.user.update({85where: { id: validated.userId },86data: {87name: validated.name,88email: validated.email89}90})9192return { success: true }93}94```9596Reference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)97