Papermark 实践指南:让 Server Actions 像 API 路由一样完成认证(Authenticate Server Actions Like API Routes) 后端前端企业应用【免费下载链接】papermarkPapermark is the open-source DocSend alternative and secure data rooms with built-in analytics and custom domains.项目地址https://gitcode.com/GitHub_Trending/pa/papermark点击查看免费下载Server Actions 是 Next.js App Router 中标记了use server的函数它们在网络层被暴露为公开端点与 API 路由具有同等的暴露面——任何人都可以直接调用绕过页面渲染、布局守卫与中间件。本篇指南以 Papermark 仓库的工程实践为背景系统讲解为什么必须在每个 Server Action 内部自行完成认证与授权并给出正确的代码模式、输入校验顺序以及 Papermark 在认证层面对应的统一实现with-session-team.ts作为参考。读完本文你将掌握防御式 Server Action的完整写法并理解 Papermark 中 RBAC 权限模型与数据房间级dataroom授权是如何落到每个 mutation 之上的。为什么 Server Actions 必须被当作公开端点对待在 App Router 中任何导出并标记use server的异步函数都会自动生成一个可供客户端调用的 HTTP 端点POST到对应的 action 路径。这意味着它们不仅能被页面内的form action{...}或startTransition触发攻击者可以直接构造请求调用该端点完全跳过 UI、跳过中间件middleware、跳过布局或页面级别的守卫。Next.js 官方文档对此有明确论断这也是本规则server-auth-actions.md的核心依据Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation.将 Server Actions 与面向公网的 API 端点同等对待并在执行变更前验证用户是否有权执行。因此认证authentication你是谁与授权authorization你能不能做必须写在 Server Action 的函数体内部绝不能依赖外层任何隐式保护。这也是本规则被标记为CRITICAL严重的原因一旦缺失等于把服务端的数据变更操作裸奔在公网之上任何人都能触发删除、修改等破坏性 mutation造成未授权访问。反模式缺少认证的 Server Action下面的写法是典型的错误示范——函数内部完全没有认证检查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 } }问题一目了然deleteUser被公开暴露后任何人都可以通过构造请求直接删除任意userId对应的用户。无论页面是否对当前用户隐藏了这个操作入口攻击者都能绕过 UI 直接命中底层端点。这正是中间件/页面守卫不足以保护 Server Action的实证。正确模式在 action 内部完成认证与授权安全的写法必须把校验逻辑放进 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 } }这段代码包含两层防线认证层verifySession()解析当前会话在 Papermark 中对应getServerSession(authOptions)拿不到有效会话立即抛出unauthorized杜绝匿名调用。授权层即便已登录也并非所有用户都有权执行同一 mutation——普通用户只能删除自己的账号session.user.id ! userId即拒绝只有管理员才能处理其他用户的删除请求。值得注意的是unauthorized抛出的应是 Next.js 识别的错误对象对应 401/403 语义而不是泛化的Error这样上层错误处理可以准确映射到状态码避免把认证失败错误地渲染成 500。先校验输入再认证最后变更完整的防御顺序Server Action 的入参同样来自不可信的客户端因此必须遵循输入校验先行的原则。推荐使用 Zod 等 schema 校验库对入参做白名单式校验只有通过校验后才进入认证与授权流程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 } }这里展示了一条可复用的安全流水线四步缺一不可步骤作用示例1. 校验输入拒绝畸形、超长或类型错误的数据updateProfileSchema.parse(data)对userId校验uuid格式2. 认证确认调用者身份verifySession()返回null即拒绝3. 授权确认调用者有权操作目标资源仅允许session.user.id validated.userId4. 执行变更只有通过全部关卡才触达数据库db.user.update(...)入参一律声明为unknown并先经 schema 解析保证后续代码拿到的都是经过约束的强类型数据而parse校验失败会直接抛出 ZodError从源头拦截注入与数据污染。同时注意email使用z.string().email()、字符串长度使用min(1).max(100)的显式边界这些约束应与数据库 schema 保持一致避免客户端能传、服务端存不下的边界问题。落地参考Papermark 的像 API 路由一样认证实现当前仓库的主体仍是 Pages Router API 路由架构而规则所倡导的端点内自证身份思想在 Papermark 中已有非常成熟的对应实现——即统一会话认证包装器 with-session-team.ts。它把先认证、再授权的流程固化为一层可复用的高阶函数可以作为你将 Server Actions 升级为同等安全等级时的设计蓝本。认证层基于 NextAuth 的会话解析Papermark 的认证配置集中在 auth-options.ts通过PrismaAdapter接入数据库并注册了 Google、LinkedIn、邮件验证码EmailProvider、PasskeyHanko以及 SAMLBoxyHQ Jackson等多类 provider。包装器在每次请求进入时调用const session await getServerSession(authOptions); // App Router 分支 // 或 Pages Router 分支 const session await getServerSession(req, res, authOptions);这正是文档示例中verifySession()的真实形态——任何端点未来包括 Server Action都必须先取得合法会话否则在resolveSessionTeam的第一步就被拦截if (!session || !session.user) { return { status: 401, message: Unauthorized }; }授权层RBAC 权限动词 数据房间级 entitlementPapermark 的授权并非简单的登录即可而是两层模型第一层角色到权限动词的映射能做什么定义在 permissions.ts。PermissionAction覆盖datarooms.read/write、documents.read/write、links.read/write、analytics.read/team、members.write、tokens.write、webhooks.write、domains.write、branding.read/write、sso.write等细粒度动词getPermissionsByRole为ADMIN、MANAGER、MEMBER、DATAROOM_MEMBER四类角色返回各自的权限集合其中未知名角色默认返回空集默认拒绝。第二层数据房间级 entitlement能访问哪些房间定义在 entitlements.ts。DATAROOM_MEMBER这类受限角色通过getAllowedDataroomIds从prisma.userDataroom读取被显式分配的数据房间列表再经canAccessDataroom校验目标dataroomId是否在授权列表内。两层模型与规则文档中的授权示例session.user.role ! admin session.user.id ! userId形成同构先验证你是谁再验证你能对哪个资源做什么。withTeam包装器把所有门禁会话、团队成员资格、requiredRoles、requiredPermissions、requiredPlan、房间 entitlement集中到 resolveSessionTeam 一处执行并对DATAROOM_MEMBER采取默认拒绝策略——被包装的路由若未显式声明所需权限受限角色一律 403从而把防止越权从依赖每个处理器自觉变成了可审计的强制约束。从包装器到 Server Action 的迁移要点对照文档规则与上述实现若在 Papermark 的 App Router 模块中引入 Server Actions安全的形态应当是把resolveSessionTeam中的同一套校验getServerSession→ 团队成员查询 → 角色/权限/计划门禁 → 房间 entitlement内联进每个use server函数而不是只依赖withTeam包装 API 路由因为 Server Action 没有请求上下文包装必须自己在函数体内重建这一防线。换句话说Papermark 现有的认证与授权逻辑可以直接复用只是执行位置必须从路由包装层下沉到action 函数内部。实践检查清单在合并任何包含 Server Action 的代码前逐项确认每个use server函数体内都有明确的会话认证如getServerSession/verifySession拒绝匿名调用认证之后还有资源级授权如角色检查、资源归属校验、或 Papermark 的权限动词 dataroom entitlement入参先经 Zod schema 校验parse而非safeParse后忽略类型与长度约束对齐数据库 schema变更类操作删除、更新、转移绝不依赖中间件、布局或页面守卫作为唯一防线错误抛出具语义的 401/403 错误而非笼统的 500破坏性操作删除、冻结等在 Papermark 中额外用requiredRoles限制为 ADMIN/MANAGER与withTeam的默认拒绝策略保持一致。把 Server Actions 当作公开 API 端点来写——认证与授权永远放在函数内部输入永远先校验变更永远最后执行。这是 Next.js 官方安全建议也是 server-auth-actions.md 中 impact 为CRITICAL的根本原因防线若不在 action 内部就等于没有防线。赞分享后端前端企业应用【免费下载链接】papermarkPapermark is the open-source DocSend alternative and secure data rooms with built-in analytics and custom domains.项目地址https://gitcode.com/GitHub_Trending/pa/papermark点击查看免费下载相关推荐Phoenix 前端安全实践像保护 API 路由一样认证 Next.js Server ActionsPhoenix 前端安全实践像保护 API 路由一样认证 Next.js Server Actions 本指南解读 Phoenix 仓库收录的 Vercel可观测性AI 评测LLMOpsAI 应用人工智能QuickRecorder macOS 录屏工具教程从安装到第一次录制QuickRecorder macOS 录屏工具教程从安装到第一次录制 QuickRecorder 是一款基于系统 ScreenCapture Kit 开发的桌面应用音视频屏幕录制Polar Web 实战像保护 API 路由一样为 Next.js Server Actions 做认证与鉴权Polar Web 实战像保护 API 路由一样为 Next.js Server Actions 做认证与鉴权 导读 本文讲解 Polar 前端代码库 cl后端前端金融科技上一篇gh0stzk dotfiles中的Scratchpad功能快速临时应用的实用工作流优化下一篇$name --创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考