资讯动态

Cap 开源录屏项目 SAML SSO 实战指南:WorkOS + NextAuth 的架构、部署与恢复流程

发布时间:2026/9/13 3:39:16 来源:尧图企业网站定制
Cap 开源录屏项目 SAML SSO 实战指南WorkOS NextAuth 的架构、部署与恢复流程【免费下载链接】CapOpen source Loom alternative. Beautiful, shareable screen recordings.项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap导读本文以 docs/saml-sso.md 为骨架系统讲解 Cap开源 Loom 替代品如何基于 WorkOS 单点登录Single Sign-On与既有 NextAuth 会话体系实现组织级 SAML SSO从客户购买流程、部署环境变量、WorkOS 回调配置到计费对账、安全边界与数据库迁移上线。读完本文你将掌握 Cap 中 SSO 权益的完整生命周期管理方式以及如何在自托管或生产环境正确配置、验证和排障这一企业级功能。文中所有实现细节均以仓库内 schema、迁移 SQL、单元测试与集成测试为佐证可直接对照阅读。一、架构总览WorkOS 桥接 SAMLNextAuth 承载会话Cap 的 SAML SSO 并不自己解析 SAML 断言而是采用WorkOS 托管认证 NextAuth 会话的分层架构WorkOS负责与各企业身份提供方IdP对接完成 SAML 协议交换、域名验证、Admin Portal 配置等重活NextAuth继续作为 Cap 应用层的会话框架通过其 workos provider 完成 OAuth 授权流程每个 Cap 组织拥有独立的付费 SSO 权益entitlement与显式的 WorkOS 组织映射。也就是说organizations表中每个开启 SSO 的组织都对应一个明确的 WorkOS organization而非全局共享SSO 计费与 Cap Pro 席位和 Signed BAA 附加服务完全独立。从计费模型看SAML SSO 是一条单独的路由在 Pro/BAA 处理之前被分流见下文webhook 分流。从数据模型可以直观看到这一设计。在 packages/database/schema.ts 中organizations表直接携带 WorkOS 映射字段并对workosOrganizationId建立唯一索引workosOrganizationId: varchar(workosOrganizationId, { length: 255 }), workosConnectionId: varchar(workosConnectionId, { length: 255 }), // ... workosOrganizationIdIndex: uniqueIndex(workos_organization_id_idx).on( table.workosOrganizationId, ),而付费权益本身存放在独立的organization_sso表中schema.ts其完整字段见下表对应迁移 packages/database/migrations/0041_saml_sso.sql字段类型/约束含义organizationIdvarchar(15), PKSSO 权益归属的 Cap 组织purchasedByUserIdvarchar(15), NOT NULL购买人仅 owner 可购买stripeCustomerId/stripeSubscriptionId/stripePriceIdvarchar(255)Stripe 客户、订阅与价格标识statusvarchar(32), 默认unpaid订阅状态快照paidThrough/currentPeriodEnddatetime已确认的付费截止时间与当前周期结束时间cancelAtPeriodEndboolean, 默认 false是否已安排在周期末取消checkoutAttemptId/checkoutCurrency/checkoutPriceId/checkoutSessionId/checkoutStartedAt若干持久化的 checkout 尝试记录重试与幂等的基础createdAt/updatedAttimestamp审计时间戳同时sso_stripe_subscription_id_idx对stripeSubscriptionId建立唯一索引保证同一笔 Stripe 订阅不会被绑定到多个组织。二、客户侧完整流程从购买到登录文档定义的端到端流程共五步理解每一步的边界对排查问题至关重要。1. 购买月付 SAML SSO 附加服务组织 owner 进入Settings → Organization → Security Compliance购买 SAML SSO 月付附加服务已有订阅的老客户自动沿用当前 Stripe 计费币种不出现币种选择器无当前订阅的 owner可选择配置好的 USD、GBP 或 EUR 金额Checkout 再次校验在打开支付会话前后端会再次校验 owner 的计费币种防止前后不一致。从源码看币种约束是按 Stripe 客户维度而非按所选产品执行的。这是因为 Stripe 不允许同一客户同时存在不同币种的有效订阅——sso-billing.test.ts 中通过多组用例验证了past_due等非终止状态订阅同样参与币种约束does not accept another currency for a %s subscription而已取消/已过期的历史订阅会被忽略允许重新选择币种。该组织所有当前产品包括其他附加服务必须使用同一币种。2. 验证域名支付确认后owner 与 admin 可使用Verify domain。Cap 会创建或复用该组织的 WorkOS organization并打开托管域名验证流程。已经验证过域名的组织会跳过此步仅支付永远不会验证域名——这是文档强调的安全边界之一。3. 配置身份提供方Set up SAML SSO会打开 WorkOS 的 SSO 配置流程由 IT 管理员在 WorkOS Admin Portal 中配置 IdP 连接如 Okta、Azure AD 等。配置期间Verify domains依然可用便于补充验证更多域名。4. 团队成员登录团队成员有三种入口在登录页选择 SAML SSO输入工作邮箱/域名直接使用组织或 IdP 的登录链接IdP 发起的访问访问/login?connection_id...会走同样的受保护授权流程注意这是 SSO sign-in/initiate-login 端点。此外已有 Cap 账户的用户可用/login?sso1显式进入 SSO。组织和 IdP 登录链接在已登录状态下也可使用但一个 SSO 身份不能挂到另一个当前已登录的账号上——必须先退出登录再以目标账号完成 SSO。源码中INTENT对象明确包含actorId: null表示匿名登录场景sso-state.test.ts中也有用例验证preserves an anonymous sign-in instead of accepting a later account保留匿名登录状态而不是接受后来附加的账号。5. 成员落位成功登录后已验证身份与 Cap 组织绑定用户被加入特定的 SSO 组织新成员获得member角色不占用 Pro 席位organization_members表中有独立的hasProSeat布尔字段见 schema.ts已有角色、席位与默认组织保持不变SSO 组织成为当前激活组织。三、部署配置环境变量、WorkOS 回调与防火墙规则环境变量以下变量须在部署环境中以 server-only 方式设置变量要求WORKOS_API_KEY目标 WorkOS 环境生产/测试对应的有效密钥WORKOS_CLIENT_ID同一环境下的客户端 IDWEB_URL、NEXTAUTH_URL规范化的 HTTPS 部署源NEXTAUTH_SECRET既有强会话密钥同时用于签发短时 SSO intent见安全边界STRIPE_SECRET_KEY、STRIPE_WEBHOOK_SECRET与 SSO 计费匹配的 Stripe 环境与 webhook 端点凭据STRIPE_SAML_SSO_PRICE_ID可选生产环境可用默认价格覆盖测试/开发环境使用独立 Stripe 价格时必填价格与币种要点默认生产价格为price_1UBJpTFJxA1XpeSsQmAOhibr该价格支持的币种金额直接从 Stripe 读取如 USD/GBP/EUR 的currency_options不在浏览器端换算遗留 SAML 价格仍被识别不会迫使老订阅升级或重新定价测试时必须使用月付、数量为 1的测试价格与测试 Stripe 密钥——生产价格 ID 在 Stripe 测试模式下不存在。WorkOS 重定向设置在 WorkOS 中为同一环境配置如下重定向用途生产 URLOAuth 回调 / redirect URIhttps://cap.so/api/auth/callback/workosSSO 登录 / initiate-login 端点https://cap.so/loginAdmin Portal 返回/成功基准地址https://cap.so/dashboard/settings/organization/security两点实践提醒Cap 会为组织提供 scope 化的 Admin Portal 返回与成功 URLAPI 生成的链接是短时 bearer 能力凭证应立即打开不得存储或记入日志完整测试 Admin Portal 时应使用 HTTPS 预览源preview origin而非本地 http 地址。Vercel Firewall SDK 限流规则文档建议配置防火墙规则rl_auth_sso_start例如每 IP 每分钟 20 次尝试。需要特别理解其语义共享限流 helper 是best-effort 且 fail-open的规则缺失或防火墙不可用时请求会放行而不是拒绝仅仅部署规则 ID 不会启用限流必须配合 SDK 调用自托管部署应提供等效的外围防护perimeter protection。四、计费与恢复幂等、对账与人工介入边界权益判定规则organization_sso记录组织、购买人、Stripe 客户/订阅、已确认付费的访问周期以及持久化的 checkout 预留。权限分层明确仅 owner 可购买或管理计费owner 与 admin 可配置 WorkOSmember 无法获取 Admin Portal 链接。权益entitlement判定精确到已付费的账单行需要受支持的 SAML 订阅 属于该订阅及其账单项item的已付款 invoice line活跃访问截止到确认的 paid-through 日期已付费的past_due订阅享有 7 天宽限期未付款、不完整、试用中、已取消和已过期订阅一律不授予访问周期末安排的取消cancel at period end可在已付周期内继续访问。这些边界在 sso-billing.test.ts 中有系统性用例覆盖active/past_due/trialing 等状态的 paidThrough 判定、past_due 保留旧 paidThrough 等。Webhook 对账而非信任事件顺序Stripe webhook 与设置页/登录页的刷新统一以当前 Stripe 状态为准对账而不是依赖事件到达顺序。关键路由规则SSO 订阅在 Pro/BAA 处理之前被单独分流——包括已在数据库绑定、但 Stripe 元数据后来发生变化的订阅。续费使用匹配的已付款账单周期重试失败时返回非成功响应让 Stripe 可以重新投递。Checkout 重试与幂等重试复用持久化的尝试记录与Stripe 幂等键。源码中幂等键格式为saml-sso-checkout-...与cap-sso-customer-...见 sso-billing.test.ts币种变更会先使先前未关闭的会话精确过期再创建新尝试对应saml-sso-expire-...幂等键不确定的响应绝不静默开启下一次支付超过 23 小时仍未关联已知 Stripe 会话的预留需要人工对账——因为 Stripe 可能清理幂等键在找到/证明此前的支付或会话不存在之前不得清除此类预留。已付费但未关联客户的恢复流程五步对于此前已付费、但数据库缺少绑定关系的客户文档要求按以下顺序人工处置独立确认确切的 Cap owner 与组织——仅凭邮箱域名匹配不足且不得批量加入既有域名用户验证 Stripe 订阅/客户、受支持的 SAML 价格、数量、已付款账单与 paid-through 周期并保留议定价格历史支付链接客户可能不同于 owner 的 Pro 客户验证确切的 WorkOS 组织与其域名证明——不得采用相似命名组织也不得从未受信请求标记待验证域名待 schema 与安全的 webhook 代码部署后创建显式organization_sso绑定并设置organizations.workosOrganizationId然后对账订阅使用compare-and-setCAS谓词并核对结果行若旧 webhook 覆盖了 owner 的 Pro 引用须独立验证原始 Pro 订阅、客户与席位数量后再恢复这些字段绝不把 SAML 订阅塞进用户的 Pro 订阅字段。其他硬性规则新建 WorkOS 组织以 Cap 组织 ID 作为externalId显式验证过的遗留映射可保留既有 WorkOS external ID计费所有权/客户变更需人工对账——应用拒绝静默地把早期支付转移给不同付款人旧 Pro-only webhook 仍部署期间不得给遗留订阅添加 SSO 元数据。五、安全边界签名 intent、互斥选择器与事务性成员供应十分钟签名 intent授权启动阶段Cap 将以下字段签名进一个十分钟有效的 HttpOnly cookieCap 组织、WorkOS 组织、connection、当前用户以及安全 return 路径。源码测试 sso-state.test.ts 验证了十小时后过期 仅允许极小未来时钟偏差SSO_INTENT_MAX_AGE篡改组织字段/换签名密钥/畸形签名均被拒绝使用host-only、Secure、HttpOnly的__Host-前缀 cookie。同时在回调侧NextAuth 还会校验OAuth state 与 PKCE。回调签发会话前检查WorkOS 原始 profile ID、实时 connection 状态、已验证邮箱域名、既有账户绑定与已付费组织。Connection 选择器互斥WorkOS 授权请求只携带经过验证的connection选择器。WorkOS 的connection、organization、provider选择器互斥因此组织归属的校验被放在 Cap 的签名 intent 内完成而不是在 provider 请求中追加第二个选择器——避免绕过组织映射校验。对应测试 sso-auth-route.test.ts 覆盖了 intent 缺失、被篡改、过期三种场景均直接返回 JSON 错误且不会调用 NextAuth。事务性与幂等的成员供应成员供应事务化且可重放使用组织/用户行锁与确定性的 WorkOS 账户键deterministic WorkOS account key只接受 SSO 组织的邀请SSO 创建的用户在注册过程中不会获得无关的个人组织或 Stripe 客户会话令牌签发前先提交成员关系避免出现已登录但无成员身份的窗口。边界与未交付能力该功能不实现以下企业控制配置前须向客户明示不强制 SSO-only 登录标准 Cap 登录仍然可用不实现 SCIM/去供应deprovisioning用户离开 IdP 后不撤销其既有 Cap 会话不提供 SAML 单点登出SLO。另外当存在歧义的默认 connection时域名发现会被阻断但设置页仍可访问以便修复显式的活跃 IdP connection 仍会对照映射组织校验。设置页与登录页直接查询 WorkOS因此禁用/重置 connection 无需依赖 connection webhook 缓存即可被识别。六、上线与验证preflight、迁移与测试套件部署前置只读预检应用迁移0041_saml_sso之前须暂停一切 WorkOS 组织绑定写入并在目标数据库的主连接上执行只读预检SELECT COUNT(*) AS duplicate_mapping_groups FROM ( SELECT workosOrganizationId FROM organizations WHERE workosOrganizationId IS NOT NULL GROUP BY workosOrganizationId HAVING COUNT(*) 1 ) AS duplicate_mappings;结果在应用任何 schema DDL 之前必须为零。该查询覆盖所有组织包括已删除tombstoned行与空字符串绑定——因为唯一约束对它们同样生效。若查询失败或返回非零立即停止不得执行任何迁移 DDL独立核验冲突的 Cap 与 WorkOS 身份用compare-and-set 更新调和绑定绝不自动删除、合并或重分配组织对账后重跑查询并在唯一约束安装前持续暂停绑定写入。预检通过后先部署增量additive迁移再部署应用变更。对既有映射组织应做对账而非静默授予未付费访问。一个环境或更早时间点的成功预检并不授权另一次部署也不得作为测试直接把 schema 推到共享本地库或生产库。迁移的生成方式迁移由 packages/database/schema.ts 生成bun run db:generate --namesaml_sso生成后须连同生成的 SQL 与 snapshot/journal 元数据一起提交见 packages/database/migrations/0041_saml_sso.sql 及其meta/0041_snapshot.json禁止手工编辑 SQL。迁移0041跟随既有 recording-jobs 迁移并保留其 snapshot 血缘。测试矩阵单元测试apps/web/__tests__/unit/sso-*.test.ts与既有 auth、mobile、Pro、BAA、webhook 回归套件并列。典型文件包括sso-auth-route.test.ts授权路由边界、intent 校验sso-state.test.tsintent 签名、过期与 cookie 属性sso-billing.test.ts币种约束、幂等键、paidThrough 判定sso-webhook.test.tscheckout.session.completed/checkout.session.async_payment_succeeded等组织级 webhook 对账sso-settings.test.ts、sso-login-pages.test.ts、sso-auth.test.ts。集成测试apps/web/tests/integration/sso-database.test.ts 使用真实 MySQL要求CAP_SSO_TEST_DATABASE_URL指向名为cap_sso_*的本地、会话级数据库并带有生成的 schema它拒绝生产/非本地 URL并保留合成夹具fixtures供检查。上线前的真实流验证清单文档特别强调单元测试、mock 与本地数据库测试不能证明真实 IdP 往返。在候选部署上须实测owner 购买/确认、admin 设置与域名验证SP 发起与 IdP 发起的登录新成员加入、既有账户登录、重复/并发登录错误域名/错误组织被拒绝原生应用native app返回流程。同时在 Stripe 测试模式检查取消、续费失败、webhook 重试、以及Pro/BAA 隔离。七、适用边界与排障要点币种冲突同一 Stripe 客户下存在冲突的当前币种订阅时应用不会猜测扣费币种需要人工支持对账未关联预留checkout 预留超过 23 小时且无已知 Stripe 会话时先找到/排除早期支付再清理限流是 fail-open不要依赖防火墙规则本身需确保 SDK 集成到位遗留映射显式验证过的映射可保留既有 WorkOS external ID但旧 webhook 未替换前不要写入 SSO 元数据。综上Cap 的 SAML SSO 是一套WorkOS 做协议、NextAuth 做会话、Stripe 做计费、organization_sso 做权益账本的清晰分层方案。无论你是部署者、自托管维护者还是二次开发集成方都可以依据本文的配置项、迁移 SQLpackages/database/migrations/0041_saml_sso.sql、schema 定义packages/database/schema.ts与测试套件apps/web/__tests__/unit/sso-*.test.ts、apps/web/tests/integration/sso-database.test.ts复现并验证整套流程。【免费下载链接】CapOpen source Loom alternative. Beautiful, shareable screen recordings.项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价