鉴权与安全契约
本文档定义 Browser、Backend、Runtime 在无状态预览链路中的鉴权边界,以及 Runtime 访问 Backend 内部 artifact 接口时的安全约束。
1. 总体原则
- 用户身份与权限只由 Backend 负责校验。
- Runtime 不维护用户体系,不解析浏览器 Cookie/JWT。
- 预览上下文统一使用短生命周期
PreviewContextToken。 - Preview 为无状态链路,不保存也不恢复 preview session。
- 预览产物访问必须同时绑定 token 中的
artifact_id、preview_kind、scope_type与入口描述。
2. Browser -> Backend
- 创建预览时,Editor 使用 HttpOnly 会话 Cookie 调用
/api/projects/{project_id}/preview-artifacts;External API 使用 Bearer PAT 调用/api/v1/projects/{project_id}/preview-artifact。创建接口负责当前用户与工作空间、项目权限校验。 - 公开展示入口
/preview/artifacts/{artifact_id}?token=...校验签名票据和 artifact 绑定,不依赖浏览器登录 Cookie。持有有效链接即可访问该短期快照;展示票据不能用来申请新的预览。 - Backend 校验通过后,反向代理到 Runtime
/__preview,并透传预览上下文 token。
浏览器错误与失效恢复
- 公开入口遇到票据或代理业务错误时,明确优先接受 HTML 的请求得到友好错误文档;JSON、通配符及禁用 HTML 的请求保留原状态码和 JSON 错误。错误文档使用
Cache-Control: no-store,不包含票据原文。 - Backend 代理给 Runtime 的
x-runtime-preview-artifact-id与x-runtime-preview-workbench-url来自服务端校验结果,用于 Runtime 应用尚未启动时回传错误和展示导航。工作台来源只取服务端公开地址或 CORS 白名单,不接受任意回跳地址。 - 错误文档发送
page-preview:error和component-preview:error,payload 包含version: 1、artifactId、message和可选code。Editor 同时校验当前 iframe 窗口、origin、协议版本及 artifact;错误码透传不改变权限语义。 - Editor 对
PREVIEW_CONTEXT_INVALID、PREVIEW_CONTEXT_REQUIRED和ARTIFACT_NOT_FOUND最多自动重建一次。替换 URL 不重置失败链预算,成功 ready、显式重试或切换目标才开启新周期;标签页重新可见时使用票据exp判断是否需要重建。 - 恢复由原入口调用原创建流程,保留页面入口、历史版本、组件源码和预览选项,并通过创建接口重新校验登录态与权限。artifact 本身也有独立 TTL,不能仅延长票据或重载旧 URL。
- 独立窗口提供返回工作台的入口;过期票据只有再次验签后才可用于生成导航,签名非法时仅返回工作台首页。独立错误页不直接续签,也不自动把历史预览转换成最新内容。
3. Backend -> Runtime
Backend 代理到 Runtime 时,必须注入:
- 请求头:
x-runtime-preview-context: <PreviewContextToken>
Runtime 通过 RUNTIME_PREVIEW_JWKS_URL 获取 JWKS 并离线验签。
PreviewContextToken 必填声明
issaudsubtenant_idartifact_idpreview_kindscope_typeworkspace_identry_descriptorasset_base_urltrace_idiatexpjti
按场景裁剪的附加声明
- 项目/页面预览额外要求:
project_id
- 组件预览可选:
component_preview_modecomponent_sourcecomponent_codecomponent_version_noruntime_kit_component_nameruntime_kit_manifest_version
组件预览约束:
scope_type=workspace_component时必须携带component_code与component_version_no。scope_type=runtime_kit_component时必须携带component_source=runtime_kit、runtime_kit_component_name与runtime_kit_manifest_version。
验签失败处理
- Token 缺失:
401 PREVIEW_CONTEXT_REQUIRED - Token 声明不完整:
401 PREVIEW_CONTEXT_INVALID - Token 过期或签名非法:
401 - manifest 与 token 的租户、artifact、scope 或入口描述不一致:
403 MANIFEST_CONTEXT_MISMATCH
4. Runtime -> Backend
Runtime 调用 Backend 内部 preview artifact API 时,统一使用服务级 JWT,并继续透传原始预览上下文:
Authorization: Bearer <RuntimeServiceAccessToken>x-runtime-preview-context: <PreviewContextToken>
Backend 应同时校验:
- 服务身份是否合法
aud是否匹配内部 API 约定- 当前请求的
artifact_id是否与 token 一致 - token 中的
preview_kind / scope_type / entry_descriptor是否与 manifest 一致 - 当前服务是否被允许读取该租户下对应 scope 的 preview artifact
5. 资源与模块白名单
Runtime 对远程模块和资源采用双层约束:
第一层:预览上下文约束
- 当前请求必须属于同一个
tenant_id + artifact_id - 项目/页面预览还必须匹配
project_id - 组件预览不得额外要求
project_id - Runtime Kit 内建组件预览必须匹配 manifest 中的
runtime_kit_component_name与runtime_kit_manifest_version
第二层:manifest 白名单
- 普通远程模块必须存在于
manifest.modules - 单页面预览的入口模块允许不进入
manifest.modules,但豁免范围仅限 token 中声明的entry_descriptor.module_path - Runtime Kit 内建组件预览不写入远程模块,
manifest.modules必须为空对象 - Runtime Kit 内建组件预览只能面向
kind=component && previewable=true的 capability - Runtime Kit doc-only 能力不生成 preview artifact,也不应获得
scope_type=runtime_kit_component的预览 token - 页面、工作空间组件和 previewSchema 只能引用 Runtime Kit manifest 公开的版本化路径;禁止引用
@runtime-kit/internal/...和未带.vN的@runtime-kit/public/... - 静态资源应优先命中
manifest.assets
6. 错误码建议
| HTTP | code | 含义 |
|---|---|---|
| 401 | PREVIEW_CONTEXT_REQUIRED | 缺少预览上下文 token |
| 401 | PREVIEW_CONTEXT_INVALID | 预览上下文声明不完整或非法 |
| 403 | MANIFEST_CONTEXT_MISMATCH | manifest 与预览上下文不一致 |
| 403 | PREVIEW_ARTIFACT_MISMATCH | token 与请求的 artifact 不一致 |
| 403 | SCOPE_CONTEXT_MISMATCH | 项目/工作空间归属与 token 不一致 |
| 404 | ARTIFACT_NOT_FOUND | preview artifact 不存在 |
| 404 | MODULE_NOT_FOUND | 模块不存在 |
| 404 | RUNTIME_KIT_CAPABILITY_NOT_FOUND | Runtime Kit capability 不存在或未启用 |
| 400 | RUNTIME_KIT_CAPABILITY_PREVIEW_NOT_ALLOWED | Runtime Kit capability 是 doc-only 或不允许预览 |
| 5xx | BACKEND_REQUEST_FAILED | Runtime 调用 Backend 内部 API 失败 |
7. 安全建议
RuntimeServiceAccessToken应由 Backend 在发起 preview/build 请求时短期签发,并通过内网请求头下发给 Runtime。PreviewContextToken.exp建议控制在分钟级。jti仅用于链路追踪与审计,不承载会话恢复语义。- Runtime 和 Backend 间建议走内网或 Service Mesh。
- 如平台支持,建议叠加 mTLS。