@alphabros/ 企业
阿尔法布罗斯工作室企业层 好一点了 1.7.10数字 :
SSO(SAML + OIDC),SCIM 2.0提供(用户+组),组织,散列链
审计日志(按键时对数据库作者的提示性说明) audit.chainKey 联 合 国
头向外导出框-见 docs/security.md(a) 执行安全政策
框架不可知管理门户——作为一个包,有四个子路径入口和一个CLI.
状态
页:1. 服务器插件, schema/CLI, 客户端帮助器, 以及所有七个门户元素都是
实施和测试(1026项测试)。 从1.0.x或0.1.x升级:改为
docs/upgrading-to-1.1.md 首先,每个变化都是默认的,
更改已有的用户所看到的, 以及替换预设插件之一的产品
将预设设置设置设置设置在自己的呼叫上(步骤2). 读取
docs/security.md 它记录了补丁的 SCIM 迁移、 符号存储、 策略
执行、审计链限制和保留/国内总产值调整态势.
安装
pnpm add @alphabros/enterprise
本套装自有附属物zod并宣布其他一切为
同伴依赖. 更好的家庭以及 better-call 是从... 从..
释放此软件包是针对( 安全标记的地板) 进行测试的, 这样您可以稍后运行
1.x发布:
{
"@better-auth/api-key": "^1.7.10",
"@better-auth/core": "^1.7.10",
"@better-auth/passkey": "^1.7.10",
"@better-auth/scim": "^1.7.10",
"@better-auth/sso": "^1.7.10",
"better-auth": "^1.7.10",
"better-call": "^1.4.2",
"@libsql/client": "^0.15.15 || ^0.17.3 || ^0.18.0", // optional — only needed for ./schema and the CLI
"drizzle-orm": "^0.45.2", // optional — only needed for ./schema
"lit": "^3", // optional — only needed for ./portal
"samlify": "^2.15.1", // peer since 1.1.0; 2.15.1+ is required for GHSA-w8r9-gp23-wcxp and others; already a dependency of @better-auth/sso
}
better-auth 联 合 国 @better-auth/* 插件集必须在 1.7.10 在1.x(已固定1.7.7)范围内
GHSA-965c-763c-88jm,而1.7.10则包括该固定. 软件包读取记录更好 - 自动写
(魔法链接飞行前读取待决链接的验证行); CI 运行全套套件
但未来改变这些记录的未成年人可能需要一个新的
释放此软件包。 升级 better-auth 和每个 @better-auth/* 组合在一起.
取消了以前的咨询例外。 见 docs/security.md
编辑 移徙指南 新的证书和身份模型.
samlify 是 SAML 库 @better-auth/sso 已使用; 软件包询问该软件包
证书 SAML 配置将被校验, 因此它必须从您的应用程序中解析..
如果您的软件包管理器没有安装对等依赖, 则明确添加 .
samlify 2.15(安全释放)是最低限度的:它只信任宣布的X.509签名证书
和检查 Audience, (中文(简体) ), Destination 财务报告和审定财务报表 Recipient 对服务商,所以
现有的 SAML 连接,其 IDP 元数据宣布没有签名证书,或者其服务供应商
实体 ID 或 ACS URL 不符合 IDP 在其回复中给出的内容, 现在无法签名 .
要求:节点 >=22, pnpm 10(或任何软件包管理器——pnpm只是这个repo自己的
dev工具链).
软件包布局
| 入境点 | 是为了什么 |
|---|---|
@alphabros/enterprise/server | 更好的自动插件预设( e)enterprisePreset及其块 |
@alphabros/enterprise/schema | Drizzle表格定义 + SQL 迁移(仅节点,见下文) |
@alphabros/enterprise/client | 更好的自动客户端插件 + home-realm 登录帮助 |
@alphabros/enterprise/portal | 用于安全设置的在线组件 |
ab-enterprise (宾 | CLI: 爱丽丝 migrate, (中文(简体) ), verify, (中文(简体) ), audit-verify (仅节点) |
从0.1升级需要新的SCIM表格,一个单独的证书HMAC秘密和 IdP再补给. 读取 迁移指南 先说.
快速启动 — 服务器
import { betterAuth } from "better-auth";
import { enterprisePreset, type Feature } from "@alphabros/enterprise/server";
export const auth = betterAuth({
database: /* drizzleAdapter(db, { provider: "sqlite", transaction: true }) */ myAdapter,
plugins: enterprisePreset({
product: "my-app",
// Reads your own billing/plan state — see "Entitlements" below.
resolveEntitlements: async (orgId) => resolveEntitlementsFromStripe(orgId),
// Encrypts the per-provider IdP secrets stored on the `ssoProvider` row
// (OIDC `clientSecret`, SAML private-key fields). >=32 chars, from your
// deployment's secret manager — never a literal in source. See
// docs/security.md for exactly which fields it covers.
secretsKey: process.env.ENTERPRISE_SECRETS_KEY!,
scimCredentialHashSecret: process.env.ENTERPRISE_SCIM_CREDENTIAL_HASH_SECRET!,
// Optional: runs on every SSO-driven JIT signup (CRM sync, welcome email, ...).
provisionUser: async (user) => trackNewEnterpriseUser(user),
audit: { retentionDays: 365 },
scim: { groupRoleMap: { "Acme-Admins": "admin" } },
}),
});
对于OIDC,通过Better Auth配置提供者的确切来源
trustedOrigins 选项或 BETTER_AUTH_TRUSTED_ORIGINS 在使用门户之前——
这是更好的奥特SSRF边界 用于发现/端点起源, 没有 发行者
允许列表。 限制IPPs租户的登记,设置 sso.allowedIssuers - 没错
适用于 issuer 财务报告和审定财务报表 到信任根时,配置实际指向:
OIDC 每一个端点(上游从租户提供的JWKS取出钥匙),用于 SAML
实体编号/输入点 和签名证书,必须固定在
sso.samlCertificateFingerprints (无针的 SAML 供应商被拒绝——关闭).
每次登录时都要重新检查存储的供应商。 所罗门群岛
默认情况下, 签名与签名提供者的已核实域域绑定
(单位:千美元)sso.domainBinding——非许可清单是租户控制; domainBinding: "off" 只在所配置的允许列表(对于 SAML, 指针) 中安全 。 见
OIDC 来源和发行人允许列表
财务报告和审定财务报表 域约束。 。 。 .
samlSpKeys 实值 没有 在名单上。 它被键入 EnterpriseOptions 并预留,但
enterprisePreset 无法消耗 : @better-auth/[email protected]是因为 sso() 没有插件级别槽
共享 SP 签名身份 每个提供者 samlConfig.spMetadata,设置于
/sso/register 时间。 自己看,放进自己的登记机构
一个横跨半角的共享身份;见 docs/sso.md适当连接它是一个P2
标注在上游.
enterprisePreset 返回完整的插件列表 : organization (小组启用), sso, (中文(简体) ),
scim, (中文(简体) ), twoFactor, (中文(简体) ), passkey, (中文(简体) ), apiKey外加这个包 enterpriseGate (权利)
执法), auditLog, (中文(简体) ), orgPolicy, (中文(简体) ), scimMembershipSchema,以及 enterpriseApi (传送门)
/enterprise/* 包装端点). 每个产品都已经安装了更好的自动处理器
需求 没有每帧服务器代码 除了这个以外.
应享权利
requireFeature(ctx, orgId, feature) 是计划行为的唯一扼杀点.
enterpriseGate 已在列出的每个路径前调用 GATED_PATHS (证监会/证监会)
注册 团队创建 API 密钥创建 审计读取 政策书写 和门户网站
/enterprise/sso/* 财务报告和审定财务报表 /enterprise/scim/tokens* ——你只需要执行
resolveEntitlements。 。 。 . Feature 实值 "sso" | "scim" | "audit_log" | "enforce_2fa" | "api_keys" | "teams"。 。 。 .
一个典型的执行地图 条纹价格/计划 在它的特性设置解锁:
import type { Feature } from "@alphabros/enterprise/server";
const PLAN_FEATURES: Record<string, Feature[]> = {
free: [],
team: ["teams", "api_keys"],
business: ["teams", "api_keys", "audit_log"],
enterprise: ["teams", "api_keys", "audit_log", "sso", "scim", "enforce_2fa"],
};
// price_... -> plan name, from your Stripe dashboard / pricing config.
const STRIPE_PRICE_TO_PLAN: Record<string, keyof typeof PLAN_FEATURES> = {
price_1AbcTeam: "team",
price_1AbcBusiness: "business",
price_1AbcEnterprise: "enterprise",
};
export async function resolveEntitlementsFromStripe(orgId: string): Promise<Feature[]> {
const org = await db.query.organization.findFirst({ where: eq(organization.id, orgId) });
const plan = org?.stripePriceId ? (STRIPE_PRICE_TO_PLAN[org.stripePriceId] ?? "free") : "free";
return PLAN_FEATURES[plan];
}
应享权利按请求予以缓存(一项) resolveEntitlements 不过,每个请求的电话
许多有门的路径/requireFeature 因此,它的安全 点击自己的数据库或一个
缓存计划在这里搜索,而不担心N+1会发出单一请求.
快速启动 — 客户端
import { createAuthClient } from "better-auth/client";
import {
enterpriseClient,
ssoClient,
organizationClient,
twoFactorClient,
passkeyClient,
apiKeyClient,
discoverHomeRealm,
} from "@alphabros/enterprise/client";
export const authClient = createAuthClient({
plugins: [
enterpriseClient(),
ssoClient(),
organizationClient(),
twoFactorClient(),
passkeyClient(),
apiKeyClient(),
],
});
// Email-first login: ask for the email, then decide SSO vs. local sign-in.
const result = await discoverHomeRealm(email);
if (result.method === "sso") {
await authClient.signIn.sso({ providerId: result.providerId, callbackURL: "/dashboard" });
} else {
// fall back to your existing magic-link / password / social sign-in
}
见 docs/sso.md 以满载的、,
包括什么 org_policy.sso_enforced 对非SSO的签名尝试. org_policy.require_2fa
也执行:对于这种类成员来说,会议只能通过一种带有以下内容的方法进行:
第二个因素(SSO默认, policy.twoFactor.satisfiedBy)或密码在上游的
twoFactor() 密码用户的报名人数
终点,直到他们.. /get-session 报告 enterprise.enrollmentRequired 为了他们。 届会
在对无法注册的用户的要求之前(在以后切换,或用户加入)
(后来)被拒绝 /get-session - ┮ auth.api.getSession - 答题 null 为了它
otop- in API- key 模拟会话, 对密钥所有者进行同样的判断( 无密码所有者的密钥必须使用)
在要求后,未输入密码持有人的密钥不产生会话)。 没有改变计划 :
升级仅需要数据迁移 0003一种产品,其包装表来自
自己的移民流出 只有一个
ab-enterprise migrate --out <folder> --from 0003 永远不要完全发射,它会重新运行 0001
财务报告和审定财务报表 0002 之后它自己的。 任何 1. 0.x 安装运行 --from 0003 甚至一个文件夹 0001 这个
命令写入: 1. 0.0 应用 0002 外边docs/migration-1-7.md
步骤4。 只有来自0.1.x的从未应用的文件夹 0002 运行 --from 0002; 平缓运行
持有此命令的文件夹 0001 没有它的 0002 停下来 问哪个.
详细情况
docs/security.md。 。 。 .
门户——嵌入管理员UI
@alphabros/enterprise/portal 登记七个框架不可知性 自定义元素 :
<ab-members>, (中文(简体) ), <ab-security-settings> (被子壳), <ab-sso-wizard>, (中文(简体) ), <ab-scim-tokens>, (中文(简体) ),
<ab-security-policy>, (中文(简体) ), <ab-api-keys>, (中文(简体) ), <ab-audit-log>他们制造了暗影DOM,并与
页:1 /enterprise/* API 直接——除此之外不需要服务器代码 enterprisePreset 见上文.
一个范围说明: API密钥是每个用户而不是每个组织. <ab-api-keys> 驱动器
上游 /api-key/list 财务报告和审定财务报表 /api-key/create,被标注为登录用户,所以
org 的安全屏幕显示和创建 那个用户的 键。 键是
用户在使用时受约束,且有过期(自1.1.0起):每一次使用都会重新检查直播
用户行(一个被禁用或被移除的用户密钥被拒绝) - USER_BANNED 编号 : USER_NOT_FOUND),键
90天后期满,除非呼叫者选择另一个寿命(apiKeys.defaultExpiresInS, (中文(简体) ),
上限 apiKeys.maxExpiresInDays,默认为 365),以及每一个解禁路径——一个SCIM
已停用, /admin/ban-user, (中文(简体) ), /admin/remove-user ——取消所有用户的密钥,而不仅仅是
他们用来做这个的,被审计为 api_key.revoked 输入用户所属的每个 org
改为: (持有 owner 任何地方的会员从不 被禁止 由SCIM种子,但他们的
键像其他人一样被吊销;它们可以签入并铸造新的键. ) 载有
API 密钥到达 允许列表 没有别的了: /get-session 超过HTTP,加上上游的
服务器侧 auth.api.verifyApiKey 电话 每一个更好的终点.. /enterprise/*,每个
证书识别路径,每个密钥,组织,SSO提供者,账户和证书书写,每个
其他改为:/list-sessions, (中文(简体) ), /organization/list, (中文(简体) ), /api-key/list, (中文(简体) ), /sso/get-provider, (中文(简体) ),
/list-accounts,... ),每一个登录流, /admin/*, (中文(简体) ), /two-factor/*,并且a的每个终点
插件您添加此软件包从未听说过( )jwt()是因为 /token, (中文(简体) ), oneTimeToken()是因为
/one-time-token/generate——答案 403 API_KEY_NOT_ALLOWED_HERE许可名单,
因为拒绝列表是无法打开它不知道的东西的; 由整合者如何使用密钥构建的
(通过 auth.api.getSession({ headers }),无其他)并拓宽
仅通过对这一一揽子方案的审查修改。 拒绝在密钥验证人本身强制执行(因此
它持有一把钥匙,但有钥匙) x-api-key 单靠头部(所以它放在
预设默认值并放在有效的饼干上; /get-session 还有一个流浪汉头
饼干的会话。 让一个密钥认证为您自己的线路上的主人, 设置
apiKeys.enableSessionForAPIKeys: true {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}在预设中而不是手绘 apiKey() - 和
大小 apiKeys.rateLimit (上游默认为每24小时10个密钥请求)——参见:
docs/security.md 用于必须保存的设置。 永远不能过期
从密钥上删除( e)/api-key/update { expiresIn: null } → 400 API_KEY_EXPIRY_REQUIRED除非
运算符设置 apiKeys.defaultExpiresInS: null. Per-org关键范围界定仍然是一个后续行动.
只从浏览器上下文导入元素 . Lit 浏览器构建引用
HTMLElement,在服务器上不存在;在服务器上发送代码中导入模块
(SSR前置, 下一个服务器组件) 扔出或 bloat 服务器捆绑 。 从 a 从导入
相反,只有客户的地方——根据舰队自己的三个Cloudflare目标堆栈进行核查
(烟雾报告) 2026-09-15-p0c-lit-portal-frameworks:
斯维尔特基特
<script lang="ts">
import { onMount } from "svelte";
import { browser } from "$app/environment";
let orgId = "acme";
onMount(async () => {
if (browser) await import("@alphabros/enterprise/portal");
});
</script>
<main style="--ab-color-primary: #0f766e">
<ab-security-settings org-id={orgId}></ab-security-settings>
</main>
天体
---
const orgId = "acme"; // do NOT import the portal module in frontmatter — see above
---
<main style="--ab-color-primary: #0f766e">
<ab-security-settings org-id={orgId}></ab-security-settings>
</main>
<script>
import "@alphabros/enterprise/portal"; // bundled by Vite, browser-only
</script>
Next.js (App路由器) (英语)
"use client";
import { useEffect, useState } from "react";
export default function SecuritySettingsPage() {
const [orgId] = useState("acme");
useEffect(() => {
import("@alphabros/enterprise/portal");
}, []);
return (
<main style={{ "--ab-color-primary": "#0f766e" } as React.CSSProperties}>
<ab-security-settings org-id={orgId} />
</main>
);
}
React需要一次性的 JSX 内在元素声明,用于您使用的每个自定义标签( 请输入此选项)
一次在一个 .d.ts,而不是每页——一个流浪者 @ts-expect-error 在别的地方变得很困难
next build 失败 :
declare module "react" {
namespace JSX {
interface IntrinsicElements {
"ab-security-settings": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement>,
HTMLElement
> & {
"org-id"?: string;
};
// ...one entry per <ab-*> element you use
}
}
}
净成本: 点出 3 + 门户元素 添加 大约 6.5 kB gzip, 代码分割到任意一条路线 使这三个框架都成为现实.
他们
门户船 没有颜色、字体或间隔 - 每一个视觉价值都是
var(--ab-*) CSS 自定义财产没有倒置,因此继承了您产品的设计
指针( a)DESIGN.md而不是强加新的:
| 脚本 | 目的 |
|---|---|
--ab-font-family | 每个门户组件的基础字体堆栈 . |
--ab-font-size | 基础字体大小. |
--ab-color-text | 初正文相相相相相相相. |
--ab-color-text-muted | 中/被修改的文本——提示,空状态,装入副本. |
--ab-color-bg | 构成部分背景. |
--ab-color-surface | 升起/ 二级表面, 例如不活动标签按钮 . |
--ab-color-primary | 主动作 / 所选状态颜色 . |
--ab-color-on-primary | 顶部的文字/icon颜色 --ab-color-primary。 。 。 . |
--ab-color-danger | 错误和破坏性状态. |
--ab-color-success | 成功/正态,例如接受邀请. |
--ab-color-border | 独立使用的边框颜色 --ab-border 缩写. |
--ab-border | 完整的边框快手(宽,样式,颜色). |
--ab-radius | 按钮、面板和控制的角半径. |
--ab-space-1 . --ab-space-4 | 相距尺度,最小到最大. |
--ab-shadow | 面板/人行道的高度阴影. |
在一个全局样式表或 :root 块; 组件消耗它们
千万别违约 全文 : src/portal/tokens.md。 。 。 .
CLI (中文(简体) )ab-enterprise(中文(简体) )
只节点(读取/写入本地文件系统并直接对libSQL进行交谈)——从 a 运行 不从边线/ 工人运行时间部署步数或本地操作 :
# Write this package's SQL migration into your product's drizzle migrations
# folder, numbered to follow whatever's already there. If the folder has a
# drizzle `meta/_journal.json`, the new file is registered in it too — that
# journal is the only thing `drizzle-kit migrate` reads to decide what to
# apply, so a file dropped in without an entry is silently skipped. Statements
# are separated by `--> statement-breakpoint`, which is what lets drizzle run
# them one at a time; it is an ordinary SQL comment everywhere else.
ab-enterprise migrate --out ./drizzle/migrations
# Upgrading from 1.0.x: always --from 0003 (the data-only 0003 is all 1.1.0
# adds), whatever wrote the folder's 0001 — a product's OWN migration (e.g. a
# hand-written NNNN_enterprise_pilot.sql) or this command — because 1.0.0
# applied 0002 outside the folder (docs/migration-1-7.md step 4).
ab-enterprise migrate --out ./drizzle/migrations --from 0003
# Upgrading from 0.1.x, where 0002 was never applied: 0002 and 0003.
ab-enterprise migrate --out ./drizzle/migrations --from 0002
# A plain run (no --from) is for a new install, or a folder whose 0002 this
# command wrote. It refuses, writing nothing, a folder holding an
# *enterprise*.sql file this command did not write, or this command's 0001
# without its 0002 — the folder alone cannot show what that one applied.
# No journal in the target folder? The command says so, and the supported
# application paths are `applyMigration()` from `@alphabros/enterprise/schema`
# or `turso db shell <db> < ./drizzle/migrations/NNNN_enterprise.sql`. Either
# way, `ab-enterprise verify` below is the backstop that proves it landed.
# Check a LIVE database has every table/column this package expects.
# Exits 1 and prints one line per gap if anything is missing.
ab-enterprise verify --url $TURSO_DATABASE_URL --token $TURSO_AUTH_TOKEN
# Prefer TURSO_DATABASE_URL / TURSO_AUTH_TOKEN env vars over --url/--token —
# CLI flags are visible in `ps` output and shell history.
# Recompute an org's audit_event hash chain and confirm nothing was tampered
# with (within the retention window — see docs/security.md). A keyed chain
# (audit.chainKey) is verified with the key from AB_AUDIT_CHAIN_KEY (env only);
# --expect-head checks the chain still holds a head exported earlier from
# GET /enterprise/audit/head (catches truncation and wholesale rewrites).
ab-enterprise audit-verify --org org_123 --url $TURSO_DATABASE_URL
AB_AUDIT_CHAIN_KEY=... ab-enterprise audit-verify --org org_123 --expect-head "$(cat head.json)"
运行 ab-enterprise verify 在每次部署之后(历史上没有实现部队迁移)
Turso,即使迁移文件本身已经实施).
@alphabros/enterprise/schema 也是同样节点——它从磁盘中读取被打包的 SQL 文件
在运行时间,而不运行在 Cloudflare 工人/工人. ./server, (中文(简体) ), ./client,以及
./portal 没有节点 并运行任何更好的地方 自己做的.
这个 workerd 测试项目test/workerd(靴子) enterprisePreset 在更好的记忆中
适配器在真正的工人运行时间中 pnpm test 运行并覆盖社会登录
启动, SAML SP 元数据, SCIM Service ProviderConfig and Groups, 以及审计写入.
文档
docs/sso.md——SSO向导流,DNS TXT验证,SAML SP元数据 URL,还有node:dns-关于工人的警告.docs/scim.md- 本地SCIM用户/组,证书旋转, 成员预测、退役和IPP设置说明.docs/security.md- 补齐SCIM迁移、秘密/存储器, 政策执行、API 密钥、审计范围和链条限制、保留/GDPR,等等 软件包的可信出版设置.docs/upgrading-to-1.1.md从1.0.x升级:升级前 检查、步骤、行为变化、新选项和错误代码.docs/releases/1.1.0.md—— 1.1.0发行笔记.
发展
mise exec node@24 -- pnpm install
mise exec node@24 -- pnpm typecheck
mise exec node@24 -- pnpm lint
mise exec node@24 -- pnpm format:check
mise exec node@24 -- pnpm test
mise exec node@24 -- pnpm build
节点 >=24 用于此 repo 工具链和 CI; 已发布的软件包保存 engines.node >=22。 。 。 .
mise exec node@24 -- <cmd> 无论全球默认值如何,都给工作版本打针.