* Refactor Gemini model display: filter old versions, group low/high pro, and improve naming * Fix potential negative minutes and refine Gemini 3 Pro grouping logic based on PR feedback * The account change process has been sped up * Address PR review: enhance error message and parameterize polling interval in process handler * Address PR review: fix potential race condition in database backup logic * fix: prevent renderer white screen and harden sentry gating * feat: localize model labels * chore: enable hoisted node-linker * chore: ignore .agent/.claude/.codex in prettier --------- Co-authored-by: Draculabo <[email protected]>
2.6 KiB
2.6 KiB
title, impact, impactDescription, tags
| 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