From e01396ba4d972c39d15307205d5dde7846f5609d Mon Sep 17 00:00:00 2001 From: chenchen Date: Wed, 10 Jun 2026 16:26:44 +0800 Subject: [PATCH] =?UTF-8?q?docs(heicode):=20=C2=A716-=C2=A720=20=E7=AB=AF?= =?UTF-8?q?=E5=88=B0=E7=AB=AF=E5=85=A8=E9=80=9A=20+=20=E2=91=A0=20APIM=20?= =?UTF-8?q?=E9=AA=8C=E6=8E=89=20+=20D-5=20=E5=8F=91=E4=BF=A1=E6=94=B6?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- ...gic-link邮箱登录-给mcp-server的对接需求.md | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/Docs/Heicode-magic-link邮箱登录-给mcp-server的对接需求.md b/Docs/Heicode-magic-link邮箱登录-给mcp-server的对接需求.md index 27ac467..f259c60 100644 --- a/Docs/Heicode-magic-link邮箱登录-给mcp-server的对接需求.md +++ b/Docs/Heicode-magic-link邮箱登录-给mcp-server的对接需求.md @@ -438,3 +438,153 @@ APIM 对 landing 这个 **GET** 的 operation 必须满足——否则浏览器 ## 15.4 一句话 **request/verify 可即刻联调;landing 的 ① APIM 我方跟进;只差 HM 回 §15.3 一句(联调发信走 A 还是 B)。** + +--- + +# 16. HM owner @zsbgnw12:经 HM 生产代理实测三端点已通(2026-06-09) + +> 收到 §14/§15。HM 侧立即对**生产**做了端到端实测:客户端 → `https://code.xinghanlab.com/api/heicode-auth/...`(HM 同源代理)→ APIM → mcp-server 三端点。结果如下(真实返回)。 + +## 16.1 实测结果(HM 代理 → APIM → mcp-server,均生产) +- `POST /api/heicode-auth/api/auth/magic-link/request` body `{"email":"hm-integ-test@example.com"}`(**未注册邮箱**)→ **200** `{"success":true,"data":{"request_id":"e1134b6c-…","state":"42YbF-…","expires_in_sec":600}}`。✅ 防枚举生效(未注册也 200)、HM `{success,data}` 信封正常、`state` 已下发。 +- `POST …/magic-link/verify` body `{"code":"bad…","state":"x"}` → **401** `{"detail":"登录凭证无效或已过期"}`。✅ 坏 code 正确拒绝。 +- `GET …/magic-link/landing?token=bad&state=x` → **400 + `text/html`** 「链接无效」页。✅ +- ⇒ **HM 同源代理对三端点全部可达、零改动**;`request`/`verify` **联调即刻可用**。 + +## 16.2 对 ① APIM 透传的范围收窄(实测旁证) +- 上面 landing 的**失败分支 HTML 已经成功经 APIM 透回浏览器**(拿到了 400 + `text/html`)。⇒ APIM 放行该 GET、且能透传 `text/html` 这两点**已被实测证明**。 +- 所以 ① 现在**唯一未验证的只剩「成功路径的 `302 Location: heicode://auth/callback?...`」是否被 APIM 原样透回**(非 http scheme 的 Location)。请你方 APIM owner 重点只验这一条;其余(放行 GET / 透 HTML)实测已 OK。 + +## 16.3 HM 回 §15.3(联调发信 A/B) +- HM 也倾向 **(A) 联调期临时开真发信**(最接近真实链路)。但「D-5 正式签字前先开发信用于**联调**」需**产品点头**——**HM owner 正在 #19 跟产品确认**,确认后回写本节并请运维置 `MAGIC_LINK_EMAIL_ENABLED=true`(测试邮箱)。 +- 在产品确认前的过渡:可先用 **(B)** 从 mcp-server pod 日志(DEBUG)取链接做纯功能验证,不阻塞 `request`/`verify` 对接与回跳后 `verify` 换 token 的逻辑联调。 + +## 16.4 HM 并行推进 +- 客户端 cc-haha:邮箱登录 UI + `heicode://auth/callback` 深链注册转发 + `state` 比对 + 同源代理调 `request`/`verify`,对**生产**端点先行(landing 成功回跳待 ① 落定)。 +- D-5 邮件基建签字:HM 在 #19 追产品。 + +## 16.5 一句话 +**HM 代理→APIM→mcp 三端点实测全通,request/verify 联调已开跑;① 只剩「heicode:// 302 透传」一条待你方 APIM owner 验;§15.3 HM 倾向 A、待产品点头。** + +--- + +# 17. HM 提议:联合端到端「模拟真实」测试(一次性,不需真发信,顺带验掉 ①)(2026-06-09) + +> 目标:不等产品签字(D-5)、不真发信,**就把"邮箱→链接→landing→302→code→verify→登录产物"整条链跑通一遍**,并**用真实成功路径验掉 ① 的唯一悬念**(APIM 是否原样透回 `heicode://` 302)。 + +## 17.1 为什么需要你们配合一步 +magic-link `token` 按设计**只在邮件里 + 你们 Redis `magic_link_token:{token}`**;`request` 响应只回 `{request_id, state}`,**不回 token**。所以 HM 单方拿不到 token,走不到成功路径。**只要你们把一个有效 token 露出来一次,后面全程 HM 驱动。** + +## 17.2 联合流程(用文档已有测试账号 `55@55.com`,role=user) +1. **HM 调** `POST .../magic-link/request {email:"55@55.com"}` → 记下返回的 `state`(token 同时进你们 Redis,TTL 600s)。 +2. **请 mcp 立即**(10 分钟内)从 Redis 读出刚生成的 token(`KEYS magic_link_token:*` 或 mock 的 DEBUG 日志行),把 **token 值**回写本节(`55@55.com` 是公开测试账号,token 一次性、读后我马上消费)。 +3. **HM 用真实 token 打成功路径**(经 APIM,`curl -i` 不跟随): + `GET .../magic-link/landing?token=<你给的>&state=<步骤1的state>` → **期望 `302` + `Location: heicode://auth/callback?code=...&state=...`**。 + - ✅ 拿到带 `heicode://` 的 302 → **① 当场验掉**(APIM 能原样透回非 http Location),D-4 定 APIM,无需回退方案2。 + - ❌ 302 被 APIM 吞/改写/Location 丢失 → **① 证伪**,按 §9.3 回退方案2(HM 托管落地页)。 +4. **HM 再打** `POST .../magic-link/verify {code:<上一步302里的code>, state}` → **期望 `200` + `{token, refreshToken, user{id,name,email,role,channelId}}`**,且与 `/api/auth/login` 逐字段一致 → **登录产物等价 + EU/计费零改动 当场坐实**。 + +## 17.3 备选(更"真实",但需产品点头) +若产品同意 §15.3-A:运维把 `MAGIC_LINK_EMAIL_ENABLED=true` 指向一个**测试邮箱**,则连"收真信→点链接"都真实跑;否则用 17.2 的"露 token"即可完成功能与 ① 验证。 + +> **请 mcp 配合 17.2 第 2 步(露一个 token)**,或告知更方便的露出方式。HM 这边随时可发起步骤 1、并在拿到 token 后立即跑 3/4 把结果(尤其 ① 的 302)回写本文件。 + +## 17.4 ⏱ HM 已发起步骤1(实时,2026-06-09) +HM 刚对 `55@55.com` 调了 `request`,生产返回: +- `request_id` = `9b93f06d-a2de-4ee5-8929-6c52708e3387` +- `state` = `c2IJAScWGyMhl5FYrprprA` +- `expires_in_sec` = 600(**约 10 分钟内有效**) + +→ **请 mcp 立即从 Redis 读出对应 token**(`KEYS magic_link_token:*` 取最新一个,或 mock DEBUG 日志里 `55@55.com` 那条链接里的 token),把 **token 值回写到本节下方**。HM 拿到后立即用 `state=c2IJAScWGyMhl5FYrprprA` 跑 `landing`(`curl -i`)验 ① 的 `heicode://` 302,再 `verify` 验登录产物。**若超过 10 分钟,HM 会重发并更新本节的 request_id/state。** + +mcp 回填 token:`uoJTHO7nbM4tGNI-mTe58OWkVQjmMEwjcXc4gOqL7ZU`(已消费,见 §18 结果) + +--- + +# 18. HM owner @zsbgnw12:联合「模拟真实」端到端测试 **全链路通过**(2026-06-09,真实返回) + +> 用 §17.2 流程 + mcp 露出的 token(`uoJTHO7n…`)对 **生产** 完整跑了一遍 magic-link 链路。**① 当场验掉,登录产物等价当场坐实。** 全部真实返回如下。 + +## 18.1 步骤3 — landing 成功路径(经 APIM,`curl -i`)→ ✅ **① 透传通过** +``` +GET https://apimtaiji.azure-api.net/api/mcp/api/auth/magic-link/landing?token=uoJTHO7n…&state=c2IJAScWGyMhl5FYrprprA +→ HTTP/1.1 302 Found + Location: heicode://auth/callback?code=7MPE4UZR7AYwhiDC4ghvhpzWKfGy3nBR&state=c2IJAScWGyMhl5FYrprprA + X-Frame-Options: DENY / X-Content-Type-Options: nosniff +``` +**结论:APIM 原样透回了非 http 的 `Location: heicode://...`,302 未被跟随/改写/吞掉。① 的唯一悬念解决 → D-4 定 `方案1-APIM`,无需回退方案2。** state 原样带回、code 一次性。 + +## 18.2 步骤4 — verify(经 HM 同源代理)→ ✅ **登录产物等价** +``` +POST https://code.xinghanlab.com/api/heicode-auth/api/auth/magic-link/verify {code:7MPE4U…, state:c2IJAS…} +→ 200 {success:true, data:{token, refreshToken, user}} + token claims: sub=b00a7b8e-… email=55@55.com role=user channelId=6e6fc470-… type=access + exp-iat = 86400s(24h)✅ + refresh claims: type=refresh, exp-iat = 604800s(7天)✅ + user: {id:b00a7b8e-…, name:"55", email:"55@55.com", role:"user", channelId:"6e6fc470-…"} +``` +**与 `/api/auth/login`(《登录接口对接文档》§2.3/§3.1)的信封、JWT claims、TTL 逐字段一致** → 不引入新会话/新计费分支,**EU/计费零改动**坐实(§1.3/§12.3 验证完成)。 + +## 18.3 整链状态 +`request → (token) → landing 302 heicode:// → verify → 登录产物` **生产全通**。功能层面 **magic-link 后端 + HM 代理 + APIM 透传 + 登录等价 = 全部验证通过**。 + +## 18.4 真正剩余(仅这两条,与功能无关) +1. **真实发信(D-5)**:现 `MAGIC_LINK_EMAIL_ENABLED=false`(本次用露 token 绕过)。真发信需产品对邮件基建签字 → 运维置 true。 +2. **客户端 cc-haha**:邮箱登录 UI + `heicode://auth/callback` 深链注册转发 + state 比对 + 调 request/verify(HM 侧客户端代码,待实现)。 + +> **一句话:模拟真实测试整条链生产跑通,① 已验掉,契约 D-4 定 APIM。只差"真发信签字 + 客户端 UI"两件非功能项。** + +--- + +# 19. HM owner @zsbgnw12 — #19 收口状态 + 仅剩 D-5 待 mcp-server 推进(2026-06-10) + +> HM 侧把工单 #19(magic-link 邮箱登录)列为「等 mcp-server + 邮件基建」。功能链路(§18)已生产验证全通,**无契约/功能阻塞**。盘点后 mcp-server 侧只剩 **D-5 真实发信** 一件。 + +## 19.1 现状(已确认,无需再动) +- request → landing 302 `heicode://auth/callback` → verify 登录产物,**生产全链路验证通过**(§18.1–18.3)。 +- 登录产物与 `/api/auth/login` **逐字段等价**(JWT claims / TTL / 信封)→ **EU/计费零改动**。 +- 契约 **D-4 = APIM 方案1**(§16/§18.1 已验)。密码登录并存、不受影响(§1)。 + +## 19.2 ⭐ 还需 mcp-server 推进/回复(仅 D-5,**#19 卡点**) +当前 `MAGIC_LINK_EMAIL_ENABLED=false`(测试期靠 response 露 token 绕过)。要真正发链接给用户,需 mcp-server 把发信打通。请在本节下方回: +1. **邮件/通知基础设施 owner 是谁**(负责账号/域名/配额/合规)? +2. **走哪家发信**(SMTP / SES / SendGrid / 阿里云邮件…)?凭据走 **secret_ref / 密钥保管库,不进代码/日志/Markdown**。 +3. flip `MAGIC_LINK_EMAIL_ENABLED=true` 的**前置清单**(发信域名验证 / 退信处理 / 按 email 频率限制 / 链接 TTL 复核)。 +4. 确认**除 D-5 外 mcp-server 侧无其它阻塞**(三端点 + landing + verify 已冻结且生产可用)。 + +## 19.3 HM 侧并行(不阻塞你们) +- 客户端 cc-haha:邮箱登录 UI + `heicode://auth/callback` 深链转发 + `state` 比对 + 调 request/verify(HM/客户端侧代码,自推)。 + +## 19.4 请回复 +mcp-server 在本节下方回 **§19.2 的 1–4**(尤其邮件基建 owner + 前置清单),HM 据此推进 #19 收尾;真发信开关由对应 owner 在前置满足后置 true。 + +--- + +# 20. mcp-server 回复 §19.2(D-5 发信收口,2026-06-10) + +> 逐条回 §19.2 的 1–4。**发信通道经我方拍板:就用现有 189.cn SMTP,送达率/企业用户收不到的问题暂不考虑**(作为已知接受风险记录在案,见 Q2/Q3)。 + +## Q1 邮件基建 owner +- **暂无独立的"邮件基建 owner"**;过渡期由 **mcp-server 运维**以现有 189.cn 通道承载发信。正式 owner / 合规签字若后续要正式运营再由产品指认——但**不阻塞本次开闸**(已接受 189.cn 现状,见 Q2)。 + +## Q2 走哪家发信 +- **维持现有 189.cn SMTP**:`smtp.189.cn:465 (SSL)`、发件 `taijiagent@189.cn`,**复用** `app/email_verification.py` 的发信通道(magic-link 不另起炉灶)。凭据 `SMTP_PASSWORD` 走 k8s secret `taiji-secrets` 的 `secretKeyRef` 注入——**不进代码 / 日志 / Markdown**(符合 §1.4 与组织密钥规则)。 +- ⚠️ **已知接受风险(经决策)**:189.cn 为运营商邮箱,**对企业/海外用户的送达率、进垃圾箱风险本期不处理**;无 SPF/DKIM/DMARC 域名背书、无退信(bounce)回收。本期仅满足"能发出链接"。后续若要正式运营再评估换 SES/SendGrid。 + +## Q3 flip `MAGIC_LINK_EMAIL_ENABLED=true` 前置清单 +| 项 | 状态 | +|---|---| +| 按 email 频率限制(60s/邮箱,独立键) | ✅ 已实现 `app/magic_link.py` `check_email_rate_limit` | +| 链接 TTL:token 600s / code 120s / 一次性 | ✅ 已实现 | +| 防枚举(限流前置、存在/不存在一视同仁) | ✅ 已实现 | +| SMTP 凭据 `SMTP_PASSWORD` 注入 | ✅ 生产已注入(secret `taiji-secrets`) | +| 发信域名验证(SPF/DKIM/DMARC)/ 送达率 | ⏸ **本期不做**(Q2 接受风险) | +| 退信(bounce)处理 | ⏸ **本期不做**(Q2 接受风险) | +- **开闸动作**(满足前置后由运维执行):确认 189.cn 的 `SMTP_PASSWORD` 有效 → 把 `k8s/prod/configmap.yaml` 的 `MAGIC_LINK_EMAIL_ENABLED` 置 `true` → 滚动重启 mcp-server 生效。`request` 即对**已存在的 role=user** 邮箱真发链接。 + +## Q4 除 D-5 外有无其它阻塞 +- ✅ **无功能/契约阻塞**。三端点 + landing + verify 已冻结、生产可用、登录产物等价已生产验证(§18,且 token 被成功路径一次性消费已独立核实)。契约 D-4=APIM 定稿。 +- 备注(非阻塞,我方收尾项):§16–§20 文档 + 两个 k8s yaml(`MAGIC_LINK_EMAIL_ENABLED` 注入 + 镜像 tag 升 `arm64-v2`)将提交并推到 Gitea `feature/chenchen`,使远端与"已部署 arm64-v2 + 开闸配置"对齐。 + +## 一句话 +**发信定 189.cn(送达率本期不考虑、已接受);开闸前置除"运维置 `MAGIC_LINK_EMAIL_ENABLED=true`"外均已就绪;mcp-server 侧无其它阻塞。** HM 可据此在 #19 收尾,真发信由运维按上表开闸。