sangwook yoo 3ac66feba2 chore: .agents/.claude 설정·CLAUDE.md·AGENTS.md git 추적 추가
gitignore 의 *.md / .claude/ 차단을 풀어 다음 파일을 git 으로 추적.
- /CLAUDE.md, /AGENTS.md (루트 에이전트 가이드)
- .agents/**/*.md (skills 문서 95개)
- .claude/settings.json (Claude Code 프로젝트 설정)
.claude/worktrees/ 는 git worktree 메타이므로 신규 ignore 라인으로 제외.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:05:03 +09:00

2.6 KiB

title impact impactDescription tags
Authenticate Server Actions Like API Routes CRITICAL prevents unauthorized access to server mutations server, server-actions, authentication, security, authorization

Authenticate Server Actions Like API Routes

Impact: CRITICAL (prevents unauthorized access to server mutations)

Server 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.

Next.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."

Incorrect (no authentication check):

'use server'

export async function deleteUser(userId: string) {
  // Anyone can call this! No auth check
  await db.user.delete({ where: { id: userId } })
  return { success: true }
}

Correct (authentication inside the action):

'use server'

import { verifySession } from '@/lib/auth'
import { unauthorized } from '@/lib/errors'

export async function deleteUser(userId: string) {
  // Always check auth inside the action
  const session = await verifySession()
  
  if (!session) {
    throw unauthorized('Must be logged in')
  }
  
  // Check authorization too
  if (session.user.role !== 'admin' && session.user.id !== userId) {
    throw unauthorized('Cannot delete other users')
  }
  
  await db.user.delete({ where: { id: userId } })
  return { success: true }
}

With input validation:

'use server'

import { verifySession } from '@/lib/auth'
import { z } from 'zod'

const updateProfileSchema = z.object({
  userId: z.string().uuid(),
  name: z.string().min(1).max(100),
  email: z.string().email()
})

export async function updateProfile(data: unknown) {
  // Validate input first
  const validated = updateProfileSchema.parse(data)
  
  // Then authenticate
  const session = await verifySession()
  if (!session) {
    throw new Error('Unauthorized')
  }
  
  // Then authorize
  if (session.user.id !== validated.userId) {
    throw new Error('Can only update own profile')
  }
  
  // Finally perform the mutation
  await db.user.update({
    where: { id: validated.userId },
    data: {
      name: validated.name,
      email: validated.email
    }
  })
  
  return { success: true }
}

Reference: https://nextjs.org/docs/app/guides/authentication