Civitai 认证体系:NextAuth 到集中式认证 Hub(auth.civitai.com)的迁移全景

发布时间:2026/9/17 20:55:23
Civitai 认证体系:NextAuth 到集中式认证 Hub(auth.civitai.com)的迁移全景 Civitai 认证体系NextAuth 到集中式认证 Hubauth.civitai.com的迁移全景【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai本篇围绕 docs/auth/auth-index.md 这份认证文档总索引展开它是 Civitai monorepo 中 NextAuth → 集中式 Hub 认证迁移的唯一入口文档。读完本文你能掌握该迁移的现状主应用已完成切换、薄 ES256 令牌模型、OAuth2/OIDC Provider 迁移被推迟、Hub/Spoke 拓扑与civitai/auth包边界的设计原理、跨域civitai.red / localhost登录的 auth-code 桥机制以及多账号切换、管理员 impersonation 等核心流程的实现方式并能按索引中的分诊结构找到部署 runbook、发布流程与待办清单等配套文档。1. 迁移背景与当前状态2026-06-17 快照索引文档开宗明义本次迁移将apps/auth部署到auth.civitai.com使其成为唯一的登录权威与 Session 颁发方sole login authority / session issuer。civitai.com同一注册域和 civitai.red因法务原因属于独立注册域都通过它完成认证Session 由一个薄 ES256civ-tokencookie加共享 Redis 中的SessionUser构成各 Spoke 应用通过 JWKS 本地验签。索引文档中 Current state (2026-06-17) 一节给出了四条权威状态结论主应用已切换NextAuth 已从主应用删除[...nextauth].ts、next-auth-options.ts均已移除应用内社交登录 UI 也一并去掉/login现在只是到 Hub 的服务端重定向薄 ES256 令牌是已交付shipped的模型它取代了部分旧文档中的胖 RS256 令牌表述这一点在索引中明确标注避免读者被过时文档误导OAuth2/OIDC Provider 迁入 Hub 的迁移被推迟2026-06-17 决策当前 Provider 在主应用中处于休眠状态signing 由是否配置了密钥这一条件门控未解决的 swap-bridge / Hub 阻塞项B1 开放重定向、B2 封禁不吊销的 no-op 桩、B4 无 Redis 时 swap fail-open、B5.env.example的 RSA vs ES256 不一致、M2 登出 device-cookie会带入生产环境在推迟的 OIDC 收敛完成前swap bridge 会一直保留这些阻塞项在 cutover review 中跟踪。索引同时说明了一个重要的文档卫生原则安全 review、cutover findings、生产 lockout 调查等记录不存放在本仓库——因为这是公开仓库任何包含未修复项的 findings 清单对攻击者而言就是一份待办清单这些记录放在私有 infra 仓库中见CLAUDE.md的 Security 一节。2. Hub ↔ Spoke 架构总览索引的 Start here 分诊组把 docs/auth/auth-hub-spoke-overview.md 标记为架构spec——描述认证在 monorepo 中应该如何工作意图而非切换步骤。其核心角色划分Hubapps/authSvelteKit 应用auth.civitai.comSession 的唯一生产方与颁发方。负责登录OAuth/邮箱、签发 session token、持有签名私钥、发布公钥JWKSSpokes其余所有应用——主应用civitai.comNext.js、civitai.red同一套 Next 代码、不同注册域、moderator 应用。Spoke 是纯消费方验证并读取 session但绝不签发civitai/auth包任何应用与 Hub 之间的唯一接口所有 Hub 交互都必须经由它完成。拓扑演进的决策来源是 docs/auth/centralized-auth-app.md2026-06-09标题即 hub issues, spokes verify 的拓扑决策文档该文档论证了为什么应用与 SDK 两者都需要应用是部署物、无法被 importSpoke 必须在本地运行的验证/接收代码只能以包的形式存在——App 颁发方 surfacePackage 验证/接收 SDK。注意该文档顶部标注 SUPERSEDED/HISTORICAL其中的 swap-token 桥与USE_HUB_SESSION已随迁移移除跨域登录现在走 OAuth authorization-code PKCE 一方桥详见 docs/auth/spoke-integration-guide.md。3. 薄 Session 令牌civ-token模型索引把 docs/auth/thin-session-token-design.md 标注为decided并明确指出它取代了 docs/auth/auth-hub-launch-checklist.md 中 #8/#11 的胖令牌表述。该设计的关键结论Cookie 只携带身份{ sub: userId, signedAt: epoch ms, jti: tokenId, iss, exp, kid }不内嵌任何用户数据。设计文档给出的决胜理由是跨根域一致性.com与.red是不同注册域、各自独立建立 cookie胖 cookie 意味着 N 份独立快照按各自节奏刷新、必然漂移薄 cookie 没有东西可漂移——每个根域都从同一个共享源解析用户天然一致。富用户按需解析Hub 是 session-user 数据的唯一生产方——用 Kysely 查询 PostgresjsonObjectFrom/jsonArrayFrom把UserprofilePicturereferralcustomerSubscription→product→price折叠成一次成形读取再做 SQL 做不了的派生tier 排序、getUserBanDetails、userSettings→allowAds/redBrowsingLevel从 system-permissions sysRedis 缓存读permissions带 degraded-skip 规则最后把富SessionUser写入共享缓存session:data2:{userId}。Hub 同时暴露GET /api/auth/identityread-through缓存热则直接返回miss 则即时生产吊销在同一调用内强制与POST /api/auth/identity服务鉴权AUTH_INTERNAL_TOKEN是唯一的缓存失效原语refresh:true可立即重新生产并返回新用户。消费端零配置其余所有应用只是消费者唯一构造器createSessionClient({ isRevoked })就是完整的消费面const session createSessionClient({ isRevoked }); const user await session.getSessionUser(token); // 读verify → 共享 redis → miss 则 GET {iss}/api/auth/identity await session.invalidate(userId); // 写bust const fresh await session.refresh(userId); // 写bust 重新生产查找 URL 是自描述的——令牌iss声明即 Hub 地址AUTH_JWT_ISSUER大部分请求命中 Redis只有缓存 miss 才落到 Hub APIHub 不可达的冷 miss 返回 null。令牌内容在包内有单一事实源。packages/civitai-auth/src/constants.ts 集中定义了认证契约常量export const SESSION_COOKIE_BASE civ-token; // 薄 session cookie刻意区别于旧 next-auth 的 civitai-token避免切换期互踩 export const DEVICE_COOKIE_BASE civ-device; // 每浏览器设备 id账号切换 device set export const LEGACY_SESSION_COOKIE_BASE civitai-token; // 旧 NextAuth cookie切换期只读 export const SECURE_COOKIE_PREFIX __Secure-; export const CIVITAI_OWNED_DOMAINS [civitai.com, civitai.red, civitaic.com] as const;文件头注释点出了这类常量的意义这里的漂移 静默的认证破坏cookie 命名碰撞曾正是这类 bug。packages/civitai-auth/src/cookies.ts 则把 cookie 名的 secure 前缀规则收敛到一处生产https名为__Secure-civ-token开发http为civ-tokenHub 与所有 Spoke 由此保持一致isSecureCookie()刻意使用||而非??读取NEXT_PUBLIC_BASE_URL || AUTH_JWT_ISSUER防止存在但为空串的环境变量导致两个应用算出不同 cookie 名、读不到彼此的 session。滚动刷新与吊销薄令牌是固定窗口超过 update age约 24h 活跃后由 Spoke 请 Hub重铸同一 session同用户、新窗口——只有 Hub 能铸。吊销模型分五层封禁/禁言走解析记录内的bannedAt/muted登出所有设备用sessionsValidAfter时间戳对比token.signedAt单会话登出走 per-jti标记全局 refresh 等价于缓存 bust真正的全局登出break-glass则是kid 密钥轮换。4. 验证策略共享密钥Path A vs JWKS 混合Path Cdocs/auth/auth-verification-strategy.md 回答了索引中是否认证变更会强制消费者重新部署这一问题。两条候选路径都保持本地验证、无每请求网络跳Path A——共享库 对称密钥Spoke importcivitai/auth用共享NEXTAUTH_SECRET进程内验 cookie。最简、延迟最低但密钥分发到每个应用爆炸半径大任一泄露的 Spoke 都能铸造合法 sessionPath C——非对称 JWT JWKS 混合Hub 用私钥签名Spoke 用缓存的公钥JWKS 端点本地验签 Redis 吊销检查。私钥永不出 Hub泄露的 Spoke无法铸币密钥轮换、Provider 变更、令牌内容变更均零消费者重部署新应用接入只需提供 JWKS URL。选型结论是目标 C、经由 A 分阶段到达A 是 C 工作量的严格前缀。实际交付时算法定为ES256 / EC P-256——该文档 2026-06-17 的更正说明明确指出已交付的 Path C 用的是 EC P-256 而非 RS256/RSA且 RSA 密钥现在会在启动时直接抛错。源码印证了这一点packages/civitai-auth/src/sign.ts 中ALG ES256assertEcP256守卫把密钥类型配错典型原因是过时的 keygen 文档转成一条可操作报错并给出正确的生成命令openssl ecparam -genkey -name prime256v1 -noout -out priv.pem openssl ec -in priv.pem -pubout -out pub.pem选择 ES256 而非 RS256 的理由写在同一文件的注释里同样是非对称 可发布 JWKS但签名只有约 64 字节RS256 为 256 字节session cookie 与 OIDC id_token 都更小。同一个 Hub 密钥同时签名 session 与第三方 Sign in with Civitai 的id_tokenmintIdTokenaud为 client_id、回显nonce防重放默认 1h 有效期任何标准 OIDC 库都可经公钥 JWKS 验证。算法切换是一个令牌格式变更文档给出了迁移窗口保证无人被登出窗口期内 Spoke 同时接受旧算法与 JWKS 新签名Hub 签发切换到新算法旧令牌最大 TTL如 30 天或强制重新认证过后停止接受旧算法并退役旧密钥。密钥轮换 runbook 的要求是同时发布新旧公钥在超过令牌最大 TTL 后再退役旧 kid。5. 包边界所有 Hub 交互只走civitai/auth架构 spec 的 §6 把包边界列为关键不变量任何应用绝不手搓对 Hub 的请求。从源码结构看packages/civitai-auth/src/的导出面与文档描述一一对应服务端→Hub 客户端createSessionClienttoken→user、invalidate/refresh、createDeviceAccountClient账号集 list/switch/removedevice-client.ts、createSessionTokenClient滚动刷新 吊销、createImpersonationClientimpersonate/exit、Hub 专用的createSessionSigner、以及createAuthVerifier浏览器→同源代理客户端civitai/auth/clientlistAccounts/switchAccount/removeAccount/impersonate/exitImpersonation。应用里触碰 Hub 的 API 路由只是薄代理转发浏览器 cookie、补上框架胶水如设置响应 cookie不内联 Hub URL 或请求形状客户端组件则永远不直接 fetch 认证端点而是走应用的AccountProvider账号上下文。动机在文档中写得很直白单一生产方 单一契约意味着 Hub 路由或 session 形状变更只触及一个包而不是几十个调用点。packages/civitai-auth/README.md 给出了接入方式与运行所需环境变量均为 schema 可选但 Spoke guard 功能上需要前两个// package.json civitai/auth: workspace:* // Next: transpilePackages: [civitai/auth]Vite: ssr.noExternal: [civitai/auth]变量用途AUTH_JWT_ISSUERHub origin——验证 JWTiss并构造登录重定向AUTH_JWKS_URIHub 公钥本地 ES256 验签AUTH_INTERNAL_TOKEN服务间密钥用于 INTERNAL 鉴权的 Hub 读穿/api/auth/identityHub 侧另需签名密钥环境变量AUTH_JWT_PRIVATE_KEYPKCS8、AUTH_JWT_PUBLIC_KEYSPKI、AUTH_JWT_KID均为 Hub 专用见 packages/civitai-auth/src/env.ts 的 schema 与 packages/civitai-auth/src/sign.ts 中createSessionSigner的缺钥即抛错逻辑。README 还列出两个典型坑Redis 可选但耦合session client 经civitai/redis读共享缓存Redis 缺席时 fail-open 到 Hub identity fetch但若设置了REDIS_URL就必须同时设置REDIS_SYS_URL部分配置会抛错并被 fail-open 捕获——表现为静默丢失缓存没有 Redis 就没有吊销不注入isRevoked时门禁只剩签名 有效期检查已登出/被封的令牌会一直解析到过期。Spoke 门禁的典型用法import { createSpokeGuard } from civitai/auth; export const guard createSpokeGuard({ require: (u) u.isModerator true }); // guard.check(cookieHeader, returnUrl) - { status: ok | login | forbidden, ... }框架适配器只需约 5 行login→ 重定向 Hubforbidden→ 403ok→ 写入locals.user。参考实现是 apps/moderator。6. 跨域登录civitai.red 与 localhost 的 auth-code 桥不同注册域看不到.civitai.comcookie这是浏览器约束而非部署问题。架构 spec §7 描述了现行机制Hub 签发短期授权码OAuth auth-code PKCE 的一方桥带着它重定向到 SpokeSpoke 在服务端到 Hub 兑换POST /api/auth/oauth/session得到自己的civ-token存自己的 cookie。localhost对生产 Hub 开发就是另一个这样的跨域 Spokecookie 的 secure 属性必须跟随Spoke 自身的服务协议http localhost ⇒ 非 secure cookie而非颁发方。该节还解释了一个曾经的真实 bugHub cookie 就是 civitai.com 的 session cookie——Hub 设Domain.civitai.com主应用对同名 cookie 设Domaincivitai.comRFC 6265 视其为同一 cookie 槽。于是为了到达 civitai.red 而登录会悄悄把 civitai.com 重指到新账号而.red上切回旧账号走的是不触碰 Hub cookie 的 device switch导致.com停在错误的账号上。一次性的、路径作用域的civ-pending记录就是为打破这层耦合而设账号切换场景下 Hub 不写自己的 cookie身份经civ-pending单次消费后送达/api/auth/oauth/authorize。包内常量印证了这个往返的两端packages/civitai-auth/src/constants.ts登录链接上的查询参数SYNC_PARAM sync-account以及 Spoke 侧落点SPOKE_AUTHORIZE_PATH /api/auth/authorize——两端都依赖这个精确字符串漂移会让 Hub 静默停止交接、回退写自己的.civitai.comsession没有报错也没有失败的测试。7. 多账号切换与管理员 Impersonation架构 spec §5 定义了两条设备级能力设备级账号切换Hub 维护每浏览器设备集——httpOnly 的civ-devicecookie 映射到 Redis setdevice:accounts:{deviceId}userId → lastSwitchedAt30 天滚动。切换的授权条件是存在活跃 session且目标账号在该设备集内且新鲜30 天没有客户端持有的凭据也没有 DB 层的 User↔User 关联因此无跨设备关联面。浏览器另保留一份持久、无凭据的名单localStorage{id, username, avatar}只用于让用户看到这里用过哪些账号超出 30 天窗口的账号仍会列出但点击需要重新登录管理员 impersonation唯一的授权是请求者本人的 session 是 moderator无内部 token、无额外凭据。Hub 为目标铸一枚携带impersonatedBy: moderatorId声明的civ-token退出读该声明并重新铸回 moderator 自己的 session。Impersonation 不触碰设备账号集审计ModActivity由应用侧写入。8. 安全不变量与文档索引导航架构 spec 的安全不变量一节是整套迁移必须始终成立的硬约束签名私钥只存在于 HubSpoke 仅验证civ-token是 identity-onlytoken 体内不含可信任的授权/角色数据角色来自解析出的用户无客户端持有的长效凭据旧的 localStorage AES token 已移除服务间 Hub 调用使用专用的AUTH_INTERNAL_TOKEN绝不使用用户 session token机密永不入库.env被 gitignore。除上述Start here、Visual reference两份 HTML 流程图UC1–UC8 登录/登出/切换/impersonation 的已实现流程与 cookie 参考、swap-token 现状 vs OIDC auth-code 提案的线路图与 SSO 简化路线docs/auth/first-party-sso-vs-oauth-analysis.md、docs/auth/auth-login-simplification.md、docs/auth/oauth-first-party-migration-plan.md之外索引文档按任务把全部认证文档分成五组构成按任务检索的分诊表Cutover 执行NextAuth → Hubdocs/auth/main-app-auth-cutover.md主应用切换状态与ship 前剥离 NextAuth决策、docs/auth/auth-hub-main-app-changes.md⚠️ 部分描述已废弃的胖 RS256 模型已被薄令牌设计取代、docs/auth/auth-hub-launch-checklist.md⚠️ #8/#11 同被取代、docs/auth/drop-main-app-social-login.md移除应用内社交登录按钮的清单已基本落地OAuth2/OIDC Provider 迁移推迟docs/auth/oauth-provider-to-auth-app.md分阶段计划、docs/auth/oauth-provider-implementation-checklist.md含 §I一方桥 → OIDC、docs/auth/oauth-scoped-tokens.md 及其 checklist/review、docs/auth/oauth-resume-state.mdfeature/scoped-tokens分支恢复状态、docs/auth/oauth-developer-docs.md对外 Civitai OAuth 开发者指南Review 与整合待办docs/auth/auth-prelaunch-action-checklist.md 是从全部文档推导出的整合 to-do阻塞项、运维缺口、包边界、文档卫生、推迟工作带来源位置2026-06-17部署入口索引顶部特别提示——正在部署的读者直接看docs/auth/auth-hub-deployment-plan.md它是当前唯一整合的部署 runbook交付内容、DB 前置、env、有序切换、风险、验证、回滚各分阶段清单都汇入它运维/杂项docs/auth/releasing.md如何切一个 Hub 发布pnpm run release:auth[:minor|:major]→auth-app-vX.Y.Ztag → 集群内 Tekton 构建 → ghcr → Flux、docs/auth/session-refresh-debug-instrumentation.mdsession-refresh 调查中临时诊断日志的追踪便于干净回退、docs/auth/post-deploy-domain-env-consolidation.md后续计划把重叠的 domain/origin 环境变量收敛到 color-map 唯一事实源、退役NEXT_PUBLIC_BASE_URL——这也与本文第 3 节isSecureCookie()读取的正是这个变量相呼应。9. 小结这份索引文档的价值不在于自身承载多少细节而在于它把一次正在进行、且有新旧表述并存风险的架构迁移收敛成了单一入口现状以 2026-06-17 快照为准薄 ES256 令牌、主应用已切换、OIDC Provider 迁移推迟、五类已知阻塞项架构意图以 docs/auth/auth-hub-spoke-overview.md 为 spec可执行的验证/签名/cookie 契约以 packages/civitai-auth 的源码为准部署动作以 docs/auth/auth-hub-deployment-plan.md 为 runbook。对维护者而言最实用的两条纪律来自索引与 spec 本身新增认证文档必须归入对应分组并附一行用途说明引用旧文档前先核对其是否被薄令牌设计取代。【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询