Protecting Server Actions and Route Handlers
A protected page does not automatically protect every backend operation associated with it. A user might directly invoke a Route Handler or Server Action.
Every sensitive operation should establish the authenticated user and verify authorization for the target resource.
For example, deleteInvoice(invoiceId) must verify that the current user owns the invoice or has the appropriate role before deleting it.
Authorization Check
export async function deleteInvoice(invoiceId: string) {
const user = await requireUser();
const invoice = await getInvoice(invoiceId);
if (!invoice || invoice.ownerId !== user.id) {
throw new Error('Not authorized');
}
await removeInvoice(invoiceId);
}💡 The operation verifies ownership at the mutation boundary rather than trusting the UI.
Key Takeaways
- Protect mutations independently.
- Check ownership or role permissions.
- Never assume route-level protection covers every backend operation.