认证服务开发
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(验证码、认证结果通知) |
一、目录与分层
Section titled “一、目录与分层”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。
二、本地开发
Section titled “二、本地开发”pnpm --filter cikio-authserver dev # wrangler devpnpm --filter cikio-authserver typecheck # tsc --noEmitpnpm --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)。
三、数据库与迁移
Section titled “三、数据库与迁移”表定义分两类,这个分工必须保持:
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 生成 SQLpnpm --filter cikio-authserver db:migrate:local # 应用到本地 D1pnpm --filter cikio-authserver db:migrate # 应用到远程 D1(--remote)pnpm --filter cikio-authserver run deploy # 最后才部署四、运行时绑定与定时任务
Section titled “四、运行时绑定与定时任务”| 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 “五、写入代码前必须知道的约束”这些是踩过的坑,不是风格偏好:
src/styles.ts是一个模板字符串:里面出现反引号会直接截断 CSS。- 浏览器端 JS 只能内联:这个 Worker 不产出静态 JS 文件,页面上的脚本通过
lib/client-scripts.ts的字符串常量或toast.ts的序列化机制注入。 配合wrangler.toml里keep_names = false(避免注入的 helper 缺失)。 - Hono 路由注册顺序是安全边界:
/api/auth/*的透传必须早于页面路由, 否则会把认证端点截走。新增路由时先确认它排在哪。 - CSP 的
form-action白名单只放了三类目标:站内、第三方登录提供方的授权页、*.cikio.cn子站回跳。 新增表单提交目标要同步改这里。 - 本地 Origin 会被改写:
wrangler.toml声明了routes后,wrangler dev会把本地请求的 Origin 重写成自定义域,所以trustedOrigins里的http://本地来源不要删,否则回调被拒。 - 限流按 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插件提供, 只有客户端请求openidscope 时才表现为 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/ 下的令牌脚本(写入服务令牌表,支持本地/远程) |
第一个管理员账号没有自助入口,需要直接在库里提权;新增管理员按同一路径处理。