让登录可靠地发生
为高可用 Kratos 接入 Telegram

我们的目标,是为 Emby 服务器实现一套高可用、高安全性的统一认证服务。让各个应用复用同一套身份、登录流程与会话管理,不再分别维护账号系统。
Kratos 是什么
Ory Kratos 是一套以 API 为核心的身份与用户认证系统。它管理用户身份、注册、登录和会话,也提供账号验证、恢复及外部身份提供方接入等能力。这里的“无头”意味着认证服务与界面分离:登录页可以按我们的需求设计,认证流程由 Kratos 处理。
为什么选择 Kratos
对 Emby 服务器来说,首先是能复用已有的身份基础设施。集群里已经有 Kratos,接入 Telegram 可以沿用本站的 identity 与会话管理,不必再围绕一个登录按钮维护另一套账号系统。
其次,它把认证与页面拆开了。我们需要自己控制门户的注册体验、补填资料和错误恢复。Kratos 的 API 让我们保留自己的界面,同时复用认证流程。
Telegram 提供 OIDC,Kratos 可以作为 OIDC 客户端接入。这样,外部身份能够进入同一套本站身份模型;以后调整某个应用允许的登录方式,也不必改变其他应用的政策。
部署方式同样适合现有环境:Kratos API 可以运行多个副本,通过共享数据库接续登录流程,配合 Kubernetes 和 GitOps 管理发布。
登录界面由我们维护,数据库和发布继续沿用现有运维体系。这个分工与现有技术栈相吻合。
接下来先验证跨副本的登录流程,再接入 Telegram、完成身份映射,并在门户后端限制登录来源。
认证服务的组成
Telegram 提供外部身份,Kratos 将它关联到本站 identity 并建立会话,门户通过 API 接入登录流程。

这是认证链路的逻辑结构。生产部署在入口设置了三副本 Tunnel connector,Kratos 与 PostgreSQL 之间使用两实例 PgBouncer。
这里的 Kratos 是 OIDC 客户端。它向 Telegram 发起认证请求,并建立本站会话;它不是给其他应用签发 OAuth access token 的授权服务器。因此,这次接入不需要先增加 Hydra。
采用这种分工,也让后续调整比较局部。我们可以让门户只接受 Telegram 登录,而不必关闭共享 Kratos 中其他应用仍在使用的密码登录。
先验证跨副本的登录流程
本次使用 Kratos v26.2.0。API 调整为三个副本,分布在三个节点。滚动更新配置为 maxSurge: 1、maxUnavailable: 0,PDB 则为自愿中断设置至少保留两个健康副本的约束。
数据库迁移单独通过升级前的 Job 执行,不再让每个 API Pod 的启动都承担迁移工作。这样,扩容和替换副本不会同时触发多份迁移任务。

部署完成后,我们创建了一条登录流程,再分别通过其他副本读取。记录显示,这条流程能在不同副本间接续;副本替换之后,也仍然能够读取。
接下来才是驱逐测试。第一次驱逐后,副本约 8.4 秒恢复。公网的 40 次健康请求全部成功,内部的 100 次请求中却有一次超过 5 秒。第二次分路径检查的 210 次请求全部成功。
首次驱逐中的超时原因仍待定位。随后我们把测试推进到生产环境,持续创建登录流程,观察应用、入口和数据库切换时的表现。
使用 Telegram 的原生 OIDC 流程
我们采用 Telegram 原生 OIDC 的标准授权码流程。
Telegram 当前官方文档提供 OIDC discovery,支持授权码与 PKCE S256,默认使用 RS256 签名。用户资料通过 ID token 提供。官方文档
对应到 Kratos,我们使用 generic provider,从 ID token 读取声明,申请 openid profile,并强制 PKCE。Client ID 和 Client Secret 来自 BotFather 的登录设置,Secret 只在服务端使用。

这张图省略了首次注册的补资料步骤。PKCE 跳转携带的是 challenge,verifier 留在服务端;只有 token 校验和必要的注册步骤完成后,才建立会话。
这里有两个配置细节值得单独检查。
首先是回调地址。在我们使用的版本与 PKCE 配置下,登记的是不带 provider ID 后缀的回调。回调地址以 Kratos 实际生成的授权请求为准,与 BotFather 登记项逐字一致。
用户资料直接取自经过校验的 ID token,以 (issuer, sub) 作为外部身份键。
Bot API 使用的 Telegram user ID 单独保存,不默认与 OIDC subject 等同。用户名、昵称和头像也不参与自动合并账号。针对较大的数字 ID,mapper 测试检查了精确保留,避免中间的浮点数转换改变身份标识。
配置上线时,我们验证了授权跳转中的 state、S256、scope 和回调。错误 state 被拒绝,登录态仅在授权完成后建立。随后用户完成了真实 Telegram 登录。
登录流程过期后,页面应引导用户重新开始,不复用失效的认证事务。
Telegram-only 要在后端执行
门户最初计划同时提供 Telegram 与密码入口,后来需求明确为只接受 Telegram。
删掉密码输入框并不够。共享 Kratos 仍支持密码登录,已经存在的密码会话也不会因为前端按钮消失而失效。
我们在门户后端检查 Kratos 原生会话的认证来源,要求其经过 Telegram OIDC。用户可编辑资料里出现 Telegram 字段,不会被当作有效的认证证据。

隔离测试里,我们创建了真实的密码来源会话。Kratos 认为它有效,门户返回 403。
已有账号绑定同样需要显式认证,不能因为同名或相同邮箱自动合并。
邮箱资料与验证

Kratos 的身份 schema 要求填写邮箱。新用户完成 Telegram 授权后,在门户补填邮箱,继续完成注册。
随后又补上了邮箱归属验证。验证码和邮件队列复用 Kratos 原生流程与 Courier,不再额外维护一套 OTP 服务。未验证邮箱的会话会被后端拦截;邮箱验证码也不能独立用于登录。
隔离环境已经走通收件、错误验证码、重发与过期恢复。生产 SMTP 已通过连接认证检查;门户的邮箱验证功能尚待上线,生产收件与队列补发也待验收。
我们怎样测试
门户集成测试使用真实 Kratos、临时 PostgreSQL 和本地签名的 OIDC fixture,走通 PKCE、签名 token、补资料、会话建立和退出。
初期浏览器回归有 68 项,后续加入邮箱验证后增长到 112 项,覆盖桌面、手机、平板和窄屏。后端还有 Go race 检查,以及显式使用隔离数据库的回归。回归覆盖:
- 密码来源会话是否被门户拒绝。
- 外部授权未完成时,能否绕过流程提交资料。
- 页面显示验证成功后,后端是否确认了实际身份状态。
- 流程过期、用户取消或服务不可用时,页面能否正确恢复。
把故障真正制造出来
2026 年 10 月 15 日,我们在生产环境持续创建登录流程,让每次合成请求都经过数据库读写,再依次重启应用、驱逐副本、更新入口和切换数据库。
| 演练 | 恢复情况 | 合成请求失败 |
|---|---|---|
| Kratos 滚动重启 | 21.7 秒完成 | 0 / 49 |
| 通过 Eviction API 主动驱逐一个 Kratos Pod | 12.4 秒恢复三副本 | 0 / 37 |
| 入口 Tunnel connector 改为三副本并滚动更新 | 无路由丢失 | 0 / 98(1 次超过 2 秒) |
| Kratos 改连独立连接池(PgBouncer,session 模式,两实例) | 切换无中断 | 0 / 54 |
| Courier 进程中断 | 4.9 秒恢复,始终只有一个 worker | 当时队列为空,没能验证补发 |
| PostgreSQL 计划内主从切换(做了两次) | 约 28 秒集群恢复健康 | 写失败窗口约 12~14 秒,期间约 20%~25% 的请求返回 500 |
从备份恢复数据库的演练:在单独的命名空间里新建一个临时集群,从现有对象存储中的全量备份和 WAL 恢复,完成后删除。数据库约 3.7 GB,需要重放约 20 小时的 WAL。
- RTO:恢复任务实际开始到数据库可读写用了约 15 分钟。整个演练耗时约 27 分钟,其中约 11 分钟浪费在临时存储卷的调度问题上。真实故障时还要加上判断故障和切换应用连接的时间,所以 RTO 应该按“半小时级”来估计。
- RPO:恢复出的数据比恢复完成时刻落后约 3 分钟。WAL 归档的超时设为 5 分钟,实际恢复点还取决于 WAL 归档和传输是否及时完成。数据库采用异步复制,主库突然损坏时存在数据损失窗口。
这轮演练里,Kratos 滚动更新与驱逐期间的合成请求全部成功。入口的三个 connector 与连接池的两个实例分散在不同节点,Kratos 由 GitOps 管理,网络策略默认拒绝、按需放行。
下一步:缩短数据库切换窗口
应用层的替换已经比较平稳,数据库切换暴露了最明显的中断:12~14 秒的写失败。接下来的工作集中在这里:
- 排查数据库切换时的连接失效、重连与请求重试。当前两实例连接池下仍存在这个写失败窗口。
- 备份和数据库均位于同一个云区域。跨区域复制因成本暂缓,区域级灾难恢复仍是后续建设项。
- 整机突发宕机演练待安排。每个数据库节点上都还有绑定在本节点的单实例服务,现在 drain 一台节点会连带影响其他业务。
- Courier 按官方要求保持单实例,下一轮验收将覆盖真实队列积压后的补发和生产邮件送达。
参考
配图由 AI 生成。