开发者中心 · 扩展平台
嵌入式页面——在后台管理中嵌入您自己的工具。
注册您自己的 https 托管页面,它将成为 SocialHub 管理员内的一个许可菜单项 - 一个全页面、沙盒 iframe。 Flash 为您的页面提供一个短暂的、无 PII 的身份令牌,以便您可以向操作员授权,而无需他们再次登录。
抢先体验 · 开放扩展模块背后
工作原理
从您的 URL 到菜单项只需四步。
注册您的页面
工作区管理员可以在 Flash → 配置 → 嵌入页面 中添加您的 https URL,并设置菜单标签、来源允许列表以及哪些角色/层级可以打开该 URL。
复制签名秘诀
Flash 会显示一次签名密钥 (slotsec_…)。将其作为服务器密钥存储在您的应用中——您需要使用它来验证 Flash 的身份令牌。
Flash 为您的页面构建框架
该页面会以侧边栏菜单项的形式出现在允许的运营商的“应用”下方;打开它会在沙盒化的 iframe 中加载您的 URL,并附加 ?flash_context=<token>。
验证 + 授权
读取令牌,在服务器端 (HS256) 进行验证,并根据令牌所包含的工作区 ID 和操作员角色来控制您的用户界面。
身份令牌
短暂有效,不涉及个人身份信息,以您的秘密签名。
在每次渲染时,Flash 都会将 ?flash_context=<JWT> 附加到您的 URL。它是一个用您的页面秘密签名的 HS256 JWT(因此您(而不是平台)是信任锚),有效期最多 120 秒,并且它不携带任何成员个人数据 - 仅包含工作区 ID 和操作员的不透明 ID + 粗略角色,因此您可以授权。
{
"iss": "flash",
"aud": "nav.page",
"slotKey": "nav.page",
"ctx": {
"teamId": "team_...", // the workspace (tenant) — the tenant already owns this
"userId": "usr_...", // opaque operator id (NOT an email/name/phone)
"role": "org_admin", // coarse role: org_owner | org_admin | marketer | store_manager
"pageType": "nav.page",
"locale": "en"
},
"jti": "…", "iat": 1730000000, "nbf": 1730000000,
"exp": 1730000110 // <= 120s after iat
}验证
信任令牌,而非请求。
在您的服务器上验证签名、颁发者 (flash)、受众 (nav.page) 和过期时间。然后根据工作区 ID 及其携带的角色进行授权 — 切勿使用 cookie(iframe 没有可用的来源;请参见下文)。
// Node (jose). The secret is your page's signing secret, shown ONCE at registration.
import { jwtVerify } from "jose";
const SECRET = new TextEncoder().encode(process.env.FLASH_EMBED_SECRET); // slotsec_…
export async function verifyFlashContext(token) {
const { payload } = await jwtVerify(token, SECRET, {
algorithms: ["HS256"],
issuer: "flash",
audience: "nav.page",
requiredClaims: ["exp", "jti"],
maxTokenAge: "120s",
});
// payload.ctx = { teamId, userId, role, pageType, locale }
return payload.ctx; // → authorize the operator with this
}// In your page: read ?flash_context=… , verify it server-side, then authorize.
const token = new URLSearchParams(location.search).get("flash_context");
// POST it to YOUR backend, verify with the snippet above, and gate the UI on
// ctx.teamId + ctx.role. Never trust it unverified, and never rely on cookies —
// the iframe is a null origin (see "Framing" below).框架与安全
您的页面必须执行的操作。
允许框架
您的页面必须允许被 Flash 嵌入。socialhub.ai — 不要发送 X-Frame-Options: DENY;设置 Content-Security-Policy: frame-ancestors https://flash.socialhub.ai.
假设null来源
Flash 使用 allow-scripts 和 no-allow-same-origin 对 iframe 进行沙盒化,因此您的页面运行在 null 源:不使用 Flash cookie、存储或父级 DOM。使用令牌进行身份验证,而不是 cookie。
postMessage 已加入允许列表
主机只接受 postMessage 仅允许来自您注册的来源,并且仅限来自您的 iframe 窗口。请严格控制允许的来源列表,并仅支持 https。
如果签名密钥泄露,请从管理员处轮换该密钥(旧令牌将立即停止验证)。嵌入式页面目前处于 早期访问 阶段,默认情况下每个工作区均已关闭,启用后需通过 SocialHub 的第三方嵌入式安全审查。
与管理员界面保持一致
打造原生体验,而非生硬附加。
您的页面在 SocialHub 管理员内部呈现,因此它应该看起来像它所属的。管理是一个单一的、扁平的、浅色的主题,建立在一些令牌上——镜像它们。 Flash 已经将您的页面包装在带有“第三方”徽章的标题窗口中,因此请勿添加您自己的顶栏、侧边栏或全页镶边;只是渲染内容。
/* Starter tokens that match the SocialHub admin (flat, light, single-accent).
Drop into your embedded page so it feels native, not bolted on. */
:root {
/* type — the admin's three families */
--font-sans: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;
--font-display: "Plus Jakarta Sans", "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
/* surfaces — light theme only (the admin has no dark mode) */
--bg: #fafbfc; /* page background */
--surface: #ffffff; /* cards / panels */
--subtle: #f7fafc; /* muted fills */
--border: #e2e8f0; /* hairlines */
/* text */
--text: #121c3d; /* primary (brand navy) */
--text-2: #4a5568; /* secondary */
--muted: #64748b; /* muted */
/* accent — the admin's primary action colour */
--accent: #2ecc87;
--accent-dark: #25b678;
/* radius + 4px spacing scale + elevation */
--radius: 8px; --radius-lg: 12px;
--space-2: 8px; --space-3: 12px; --space-4: 16px; --space-6: 24px;
--shadow-sm: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
}
body { margin: 0; background: var(--bg); color: var(--text);
font: 14px/1.5 var(--font-sans); }
h1, h2, h3 { font-family: var(--font-display); color: var(--text); }
.card { background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
padding: var(--space-4); }
.btn { background: var(--accent); color: #fff; border: 0; font-weight: 600;
border-radius: var(--radius); padding: 8px 14px; cursor: pointer; }
.btn:hover { background: var(--accent-dark); }
code { font-family: var(--font-mono); color: var(--text-2); }无需您自己的 Chrome 浏览器
Flash 提供窗口框架和徽章。跳过应用程序的全局导航/侧边栏/页眉 — 仅渲染工具本身。
轻薄、扁平、单一强调色
仅限浅色主题(管理员无深色模式):#fafbfc 页面上的白色卡片,极细的 #e2e8f0 边框,一个绿色点缀 (#2ecc87)。
填充宽度,流动高度
使用全宽度并让内容自由流动;主机将调整框架大小(大致为整个视口高度)并支持滚动——不要强制使用 100vh 或固定页脚。
优雅降级
如果您没有自行托管字体(Inter / Plus Jakarta Sans / JetBrains Mono),则会回退到系统 UI; Honor 偏好减少动态效果,并偏好浅色配色方案。
这些值跟踪当前的管理主题;将它们作为起点,并在重要的地方保留自己的品牌。我们的目标是让页面看起来像 SocialHub 的一个界面,而不是进入不同产品的窗口。
一个完整的示例
一个功能齐全的嵌入式页面,约 30 行代码即可完成。
一个可直接运行和注册的极简 Express 服务器:它允许 Flash 构建框架、验证身份令牌、根据操作员角色进行授权,并渲染一个作用域限定于工作区的工具。将管理器中的签名密钥复制到 FLASH_EMBED_SECRET 并运行它。
// server.js — a complete, minimal SocialHub Embedded Page (Express + jose).
// Register https://<host>/embed in Flash → Settings → Embedded Pages, allow the
// origin https://<host>, pick roles/tier, then copy the signing secret into env.
import express from "express";
import { jwtVerify } from "jose";
const app = express();
const SECRET = new TextEncoder().encode(process.env.FLASH_EMBED_SECRET); // slotsec_…
const FLASH = "https://flash.socialhub.ai";
app.get("/embed", async (req, res) => {
// 1. Let ONLY Flash frame this page.
res.setHeader("Content-Security-Policy", "frame-ancestors " + FLASH);
// 2. Read + verify the identity token Flash appended (?flash_context=…).
let ctx;
try {
const { payload } = await jwtVerify(String(req.query.flash_context || ""), SECRET, {
algorithms: ["HS256"], issuer: "flash", audience: "nav.page",
requiredClaims: ["exp", "jti"], maxTokenAge: "120s",
});
ctx = payload.ctx; // { teamId, userId, role, pageType, locale }
} catch {
return res.status(401).send("<p>Unauthorized — open this from inside SocialHub.</p>");
}
// 3. Authorize on the VERIFIED role (never on the raw request).
const canEdit = ctx.role === "org_owner" || ctx.role === "org_admin";
// 4. Render your tool, scoped to ctx.teamId. (Values are verified + opaque;
// still escape anything user-supplied you add.)
res.send(
'<!doctype html><meta charset="utf-8"><body style="font:14px system-ui;padding:24px">' +
'<h1>Returns portal</h1>' +
'<p>Workspace <code>' + ctx.teamId + '</code> · operator <code>' + ctx.userId +
'</code> · role <b>' + ctx.role + '</b></p>' +
(canEdit ? '<button>Process a return</button>' : '<p>Read-only for your role.</p>') +
'</body>'
);
});
app.listen(3000, () => console.log("Embedded page on :3000/embed"));1 · 允许框架
frame-ancestors https://flash.socialhub.ai — 因此只有 SocialHub 管理员可以对其进行 iframe。
2 · 验证令牌
读取 ?flash_context,验证 HS256 + 发行方 flash + 受众导航页面 + ≤120 秒过期时间;否则拒绝。
3 · 授权
Gate 功能在已验证角色上(org_owner / org_admin / …),绝不在原始请求中。
4 · 渲染,范围限定
为 ctx.teamId(即打开工具的工作区)渲染您的工具。
然后在 Flash → 设置 → 嵌入式页面 中:添加 URL(例如 https://tools.yourapp.com/embed),列出相同的来源,选择可以打开它的角色 + 层,然后将一次性签名密钥粘贴到您的服务器环境中。它现在显示在您允许的操作员的应用下。