Repository navigation
feat(seat-device): entry links in the device authorization answer (E-1) - #556
Merged
Merged
Conversation
The authorization answer now carries entryLinks {signIn, activateMember,
signUp} next to verificationUrl (server design §8.3). All links come from
the resolved APP_URL origin, never from the request; a non-https APP_URL
still answers 503 before anything is stored, so no link leaves. The
activation link is null while its page is switched off; signUp stays null
because Core has no sign-up page and no sign-up address is configured.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XEu1dNhDYNsyuhABjFw6Tn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
目标 / Goal
坐席桌面客户端设备授权(D1)的跟进片 E-1:在设备授权发起应答里下发入口地址
entryLinks(服务端设计 §8.3),让客户端不写死任何网址也能显示「去登录」「去激活」引导。地址只由APP_URL派生,属于 Core 的 D1 设备契约。E-1 of the seat desktop device authorization (D1): the authorization answer now carries
entryLinks, derived only fromAPP_URL, so the client needs no built-in website address.变更与依赖
main@103fb167(含 2a feat(seat-device): D1 2a 五个设备端点(默认关闭) #551、2b feat(seat-device): D1 2b 网页配对确认页与密码证明(默认关闭) #554 / feat(seat-device): D1 2b 设备列表、网站撤销与确认页评审修正(默认关闭) #555);head 见本 PR 最新提交。HELM_SEAT_DEVICE_AUTH_ENABLED。不为"true"时端点照旧在读请求体之前答 404、空体。不新增 env、表、迁移、错误码。seatDeviceEntryLinks()在lib/seat-device/contract.ts;authorize只调用它。做了什么
lib/seat-device/contract.tsSEAT_DEVICE_SIGN_IN_PATH(/login)、SEAT_DEVICE_ACTIVATE_MEMBER_PATH(/activate-member)与纯函数seatDeviceEntryLinks(origin, env)lib/seat-device/device-routes.tsauthorize应答加entryLinks,与verificationUrl用同一个已解析的APP_URL源lib/seat-device/contract.test.ts"true"时出现;signUp恒为nulllib/seat-device/seat-device-routes.mysql.test.tsx-forwarded-host/x-forwarded-proto下链接不变、激活开关、客户端传地址被拒、非 https 时 503 且不带任何链接应答样例(占位域名)
APP_URL=https://seat.example.test,HELM_AUTH_MEMBER_ACTIVATION_ENABLED="true":entryLinks三个键总是都在;值为字符串或null。activateMember:成员激活页/activate-member只在HELM_AUTH_MEMBER_ACTIVATION_ENABLED === "true"时存在,否则 404;所以开关不开时为null,不下发一个打不开的地址。signUp:本片恒为null(理由见「与设计不一致处」)。客户端按设计 §8.4 显示「请在网站注册」,不显示链接。verificationUrl同源(即APP_URL的源),客户端「与authBaseUrl同源」的检查可以直接复用。verificationUrl的旧客户端不受影响。错误码与 HTTP 状态(发起端点,供客户端对齐;本片未新增)
HELM_SEAT_DEVICE_AUTH_ENABLED不为"true";或非 POST 方法no-store{"error":{"code":"invalid_input"}}no-storeclientType不是seat_desktop{"error":{"code":"client_not_supported"}}no-store{"error":{"code":"rate_limited"}}no-store、Retry-AfterAPP_URL缺失、不是 https(本地开发的回环地址除外)、带路径 / 查询串 / 账号;配对码重抽耗尽;其他意外失败{"error":{"code":"temporarily_unavailable"}}no-store、Retry-After非 https 时失败即关:
APP_URL在写库之前解析,解析失败直接 503,不建待批行,verificationUrl与entryLinks都不下发(已有测试扩充为断言整个应答体只有错误码)。逐项对照(任务书 / 设计 §8)
entryLinks:登录、激活、注册signIn、activateMember、signUp三键,与设计 §8.3 第 2 条的字段名一致verificationUrlentryLinks里重复APP_URL派生,与请求 Host 无关(设计 §8.4)resolveTrustedPublicOrigin(process.env)的结果,不读request.url、Host、转发头post()带敌意 Host / 转发头,链接仍是APP_URL/login、/activate-member两页都已在 Coretemporarily_unavailableentryLinks、signUpUrl、verificationUrl等字段一律 400HELM_SEAT_DEVICE_AUTH_ENABLEDdevice-routes.test.ts与设计不一致处
signUp恒为null,不新增HELM_SEAT_DEVICE_SIGNUP_URL。 设计 §8.3 把它写成「提议」的部署配置,允许与APP_URL不同源;任务书要求「地址一律由APP_URL派生」,任务书优先。Core 没有注册页。字段先占位为null,以后加配置是兼容变更。NODE_ENV为development/test时,resolveTrustedPublicOrigin仍接受http://localhost等回环地址,链接随之为 http;这与 2a 的verificationUrl一致,也对应客户端「未打包的开发运行允许回环地址」。生产环境非 https 一律 503。/device页上的引导链接不在本片。 未登录时访问/device会先跳到/login;而/login不受设备开关控制,在那里加链接会改动所有站点的登录页。是否做、怎么做留给 owner 定。变异(9 个,全部变红,逐个还原;结束时工作区干净)
request.url的源"false"、"1"等)/signin/device路径entryLinksAPP_URL无效时退回请求的源(失败即开)signUp取自环境变量验证证据
npm run typecheck1638dbe9,Node 25.9.0npm run lint:strictnpm run check:boundariesnpm run check:public-docsnpm run check:public-releasenpm run check:secret-historynpm run test:seat-device:mysql(SEAT_DEVICE_MYSQL_REQUIRED=1,缺库即失败)npm run testscripts/sarp-proof.test.ts、scripts/golden-path-proof.test.ts的「输出目录不能在临时目录内」:本地工作树本身就在系统临时目录下,与本 PR 无关;exact-head CI 的 Test 作业「Run unit tests」通过(run 43378230780),即这 4 条在 CI 上通过.github/workflows/ci.yml的「Verify the seat device authorization store against MySQL」步骤运行npm run test:seat-device:mysql;单测由npm run test全量运行1638dbe9| 双语混写 |
npm run check:bilingual-mixing| 同上 | exit 0,无新增(21 条均在基线内) |未运行命令及原因 / Commands not run and why:
npm run build、npm run e2e、npm run self-check、npm run quality:regression、npm run db:reset:本地未运行。本片只改一个 API 应答的字段与对应测试,无页面、无构建配置、无迁移;构建与端到端以 CI 结果为准。新增
test:*:无(新用例落在已接入 CI 的既有测试文件里)。审计自闭环
POST /api/public-auth/seat-device/device-authorizations;链接只取已解析的APP_URL源;无权限、幂等、SQL collation 变化。seat-device-routes.mysql.test.ts,CI 链为ci.yml「Verify the seat device authorization store against MySQL」→npm run test:seat-device:mysql→ vitest;SEAT_DEVICE_MYSQL_REQUIRED=1,缺库报错不跳过。单测由npm run test全量覆盖。HELM_AUTH_MEMBER_ACTIVATION_ENABLED已登记在.env.example。check:public-release、check:public-docs、check:secret-history、check:bilingual-mixing均 PASS,无新增命中。example.test。npm run test的 4 条失败见上表)。1638dbe9CI 20/20 check-run success(含真库步骤与全量单测)。check:public-release、check:secret-history无新增命中;示例只用example.test占位域名。规模
4 个文件,+72 / −5(含测试)。
边界检查 / Boundary Check
风险、回滚与交付边界
entryLinks。entryLinks的读取(客户端尚未实现,接口清单 §9.4);Windows 与安装包环境。评审与合并纪律 / Review And Merge Discipline
main;作者不合并。--no-verify、未跳过 CI 或本地硬门禁。截图或录屏 / Screenshots Or Recording
N/A(无 UI 变化)。
贡献权利 / Contribution Rights
Owner Gate
no(本片可单独合入);后续两个选择题见作者回报。🤖 Generated with Claude Code
https://claude.ai/code/session_01XEu1dNhDYNsyuhABjFw6Tn