跳转到内容

认证服务开发

authserver 是 cikio 的统一身份中心,所有站点的账号、会话与 OAuth 2.0 / OIDC 能力都由它提供。 它是有服务端状态的 Worker:页面、接口、数据库、定时任务都在这里。

项 值
包名 cikio-authserver
线上地址 https://authserver.cikio.cn
技术栈 Hono 4 + better-auth 1.7 + @better-auth/oauth-provider + Drizzle ORM
存储 D1(账号、会话、OAuth client、审核留痕)、KV(限流与词库快照)、R2(头像)
邮件 Resend(验证码、认证结果通知)
authserver/
├─ src/index.ts # Hono 入口:安全头、错误/404 兜底、/api/auth/* 透传、/health、挂载子应用、定时任务
├─ src/auth.ts # better-auth 实例:Env 类型、插件、社交登录、数据库钩子、trustedOrigins
├─ src/styles.ts # 全站 CSS(导出为单个字符串)
├─ src/db/ # 数据层:index.ts(Drizzle D1 客户端)、schema.ts(汇总)、各表定义文件
├─ src/lib/ # 业务逻辑(无 JSX):认证上下文、权限、封禁、注销、审核、邮件、头像…
│ └─ email/ # 邮件发送层与模板
├─ src/routes/ # .tsx 路由子应用:解析请求 → 调 lib → 渲染
├─ src/pages/ # .tsx 页面与组件(纯渲染)
├─ migrations/ # drizzle-kit 产出的 SQL + meta/
├─ scripts/ # 运维脚本(如生成服务间令牌)
└─ public/ # 静态资源(favicon 等),由 [assets] 直出

分层原则固定为:pages/ 只渲染,routes/ 处理请求,lib/ 放业务逻辑,db/ 只做数据访问。 不要把 SQL 写进页面,也不要在 lib/ 里拼 HTML。

终端窗口
pnpm --filter cikio-authserver dev # wrangler dev
pnpm --filter cikio-authserver typecheck # tsc --noEmit
pnpm --filter cikio-authserver build # wrangler deploy --dry-run(部署预检)

wrangler dev 使用本地模拟的 D1 / KV / R2,不需要远程资源、也不需要先建桶或库, 改本地数据前先停掉 dev 进程(本地 D1 文件被占用时迁移会失败)。

机密不落仓库:仓库里没有 .dev.vars,本地联调时自己按需配置,不要提交。 这个 Worker 需要以下机密键名(值一律通过 Cloudflare 侧注入,见 Monorepo 总览):

键名 用途
BETTER_AUTH_SECRET 会话与令牌签名,缺失时服务直接拒绝启动
DELETION_HASH_SECRET 注销流程里的邮箱 HMAC
MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET / MICROSOFT_TENANT_ID Entra 登录(tenant 默认 common)
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET GitHub 登录
RESEND_API_KEY / RESEND_FROM / RESEND_REPLY_TO 邮件发送

非机密配置写在 wrangler.toml 的 [vars](如 PUBLIC_BASE_URL)。

表定义分两类,这个分工必须保持:

  • src/db/auth-schema.ts 由 better-auth CLI 生成,跑 db:generate 时会被整体覆盖 —— 不要手改;
  • 手写的业务表(审计、状态、links、资料等)各占一个独立文件,并在 src/db/schema.ts 里汇总 re-export; 一旦写进生成文件所在的路径,下次生成就丢了。

迁移三步,顺序不能反:

终端窗口
pnpm --filter cikio-authserver db:generate # auth CLI 生成 schema + drizzle-kit 生成 SQL
pnpm --filter cikio-authserver db:migrate:local # 应用到本地 D1
pnpm --filter cikio-authserver db:migrate # 应用到远程 D1(--remote)
pnpm --filter cikio-authserver run deploy # 最后才部署
binding 类型 用途
DB D1 账号、会话、OAuth client、审计等主库
KV KV 限流、会话辅助数据
AVATARS R2 用户头像
MODERATION_DB D1 共享审核库(留痕)
MODERATION_WORDS KV 共享词库快照
  • 静态资源由 [assets] 直出(public/)。
  • 有一个每日触发的 Cron(清理类任务),写这类逻辑时注意它在独立入口执行、没有请求上下文。
  • 自定义域 authserver.cikio.cn 在 wrangler.toml 里以 routes + custom_domain 声明。

五、写入代码前必须知道的约束

Section titled “五、写入代码前必须知道的约束”

这些是踩过的坑,不是风格偏好:

  1. src/styles.ts 是一个模板字符串:里面出现反引号会直接截断 CSS。
  2. 浏览器端 JS 只能内联:这个 Worker 不产出静态 JS 文件,页面上的脚本通过 lib/client-scripts.ts 的字符串常量或 toast.ts 的序列化机制注入。 配合 wrangler.toml 里 keep_names = false(避免注入的 helper 缺失)。
  3. Hono 路由注册顺序是安全边界:/api/auth/* 的透传必须早于页面路由, 否则会把认证端点截走。新增路由时先确认它排在哪。
  4. CSP 的 form-action 白名单只放了三类目标:站内、第三方登录提供方的授权页、*.cikio.cn 子站回跳。 新增表单提交目标要同步改这里。
  5. 本地 Origin 会被改写:wrangler.toml 声明了 routes 后,wrangler dev 会把本地请求的 Origin 重写成自定义域,所以 trustedOrigins 里的 http:// 本地来源不要删,否则回调被拒。
  6. 限流按 IP,测试限流逻辑时注意 KV 的 TTL 下限(过短的探针会拿到假结果)。

六、OIDC 能力(对开发者的接口面)

Section titled “六、OIDC 能力(对开发者的接口面)”
  • better-auth 的全部端点挂在基础路径 /api/auth 下。
  • 发现文档是 /api/auth/.well-known/openid-configuration(basePath 是路径拼接,不是根路径); 签名公钥集在 /api/auth/jwks,由 jwt() 插件提供。
  • OIDC Provider 由独立包 @better-auth/oauth-provider 的 oauthProvider 插件提供, 只有客户端请求 openid scope 时才表现为 Provider。旧的 oidcProvider 插件已废弃(有安全公告),不要再用。
  • 客户端凭据存在 D1 的 oauthClient 表:client_id、client_secret、redirectUris、scopes; 可用 scope 为 openid / profile / email / offline_access。
  • 客户端 client_secret 在库里是哈希存储,PKCE 默认强制;手写 INSERT 造客户端时必须存哈希、不能塞明文。
  • 面向接入方的完整步骤见接入认证服务。
动作 命令
部署 pnpm --filter cikio-authserver run deploy
部署预检 pnpm --filter cikio-authserver build(wrangler deploy --dry-run)
核对线上版本 pnpm --filter cikio-authserver exec wrangler deployments list --name cikio-authserver
回退 wrangler rollback <version-id>
灰度 wrangler versions deploy
生成服务间令牌 跑 scripts/ 下的令牌脚本(写入服务令牌表,支持本地/远程)

第一个管理员账号没有自助入口,需要直接在库里提权;新增管理员按同一路径处理。

相关:认证服务(产品视角)、接入认证服务、开发问题与踩坑记录。