Skip to content

fix(auth): the forgot-password page says a reset link can't be emailed on a deployment that sends no mail (#30366) - #30406

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-k9z05a
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-k9z05a

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

domain:ui#3 · os-bill · session_01Utp6CbPN5UYMnBaNJPJWfG

Fixes #30366

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

  • 改了什么:忘记密码页在服务端报告 features.emailDelivery: false(没接邮件服务或只用 log 传输)时,提交后不再说「已发送重置链接」,改为「此处无法通过邮件发送重置链接,请联系管理员在 设置 → 用户 中为你设置新密码」,十种语言齐全。
  • 为什么改:不发邮件的部署上,用户会一直等一封永远不来的邮件;服务端一半(Forgot password says the reset link was sent on a deployment with no email service #30107,PR fix(auth): say on /auth/config whether email leaves the deployment, so forgot-password can stop claiming a link was sent #30365)已经提供这个开关,这里是界面一半。
  • 风险与回滚:只有明确的 false 才换文案;老服务端不带这个字段、或取配置失败,保持现在的文案。新文案不含地址、与提交结果无关,不会泄露账号是否存在。回滚即撤销本 PR。
  • 先看:ui/packages/auth/src/ForgotPasswordForm.tsx(成功页分支)、ui/packages/auth/src/__tests__/forgot-password-success-message.test.tsx(新 describe 块)、ui/apps/console/src/pages/auth/ForgotPasswordPage.tsx(两行 label 传递)。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/auth/src/__tests__/forgot-password-success-message.test.tsx,预期 10/10 通过。
  • 需要你做的:无,席位落地。

What changes

  • ForgotPasswordForm reads features.emailDelivery from the /auth/config answer it already fetches (alongside phoneNumberOtp). On an explicit false the success screen shows noDeliveryTitle / noDeliveryDescription — "A reset link can't be emailed here" / "This deployment doesn't send email, so no password reset link can reach you. Ask an administrator: they can set a new password for you from Setup → Users." — with an amber icon instead of the green one. The submit still goes to the server, so a log-transport deployment keeps writing the link to its console.
  • ForgotPasswordFormLabels gains noDeliveryTitle and noDeliveryDescription; AuthPublicConfig.features gains emailDelivery (the server half's AuthFeaturesConfigSchema.emailDelivery).
  • New strings auth.forgotPassword.noDeliveryTitle and auth.forgotPassword.noDeliveryDescription in all ten locale packs, passed through by the Console's forgot-password page (label wiring only, per the claim's file-surface ruling) and by DefaultForgotPasswordPage in @object-ui/app-shell.
  • Changeset: @object-ui/auth, @object-ui/i18n, @object-ui/app-shell, @object-ui/console, all patch (Metadata-protocol none).

Why

On a deployment with no mail transport, every reset request ended on "Check your email — we've sent a password reset link to …", and nothing arrived. The server half (#30107, PR #30365) now says whether mail leaves the deployment; this is the page reading it.

An absent key keeps today's text. The spec text for the flag says absent means not live, but on the wire an absent key means a server older than the flag, and most of those do send mail; telling their users to find an administrator would be the larger harm. A failed config fetch is the same unknown. So only emailDelivery === false swaps the screen, and both unknowns are pinned to keep the "check your email" text.

No account-existence oracle. The new text names no address and is chosen by the deployment flag alone, never by the reset request's answer (which is the same for known and unknown addresses server-side). A test submits a known and an unknown address on a no-mail deployment and asserts the two screens are byte-identical and carry no address.

Tests

  • forgot-password-success-message.test.tsx — new top-level describe (objectstack#30366) in the existing harness file: no-mail screen text and no address; known vs unknown address read identically; caller-supplied labels used; emailDelivery: true, absent key, and failed fetch each keep the old screen.
Local runs (head 74302c6 after merging origin/main; the merge brought no ui/ paths)
  • pnpm --dir ui exec vitest run packages/auth/src/__tests__/forgot-password-success-message.test.tsx …/identifier-trim.test.tsx …/phoneTouchTargets-29080.test.tsx — Test Files 3 passed, Tests 14 passed.
  • pnpm --dir ui exec vitest run packages/i18n/ — Test Files 95 passed, Tests 1443 passed / 13 skipped (key parity included).
  • pnpm exec turbo run type-check --filter=@object-ui/auth --filter=@object-ui/app-shell --filter=@object-ui/console --concurrency=2 — Tasks 76 successful, 76 total.
  • eslint on the changed ts/tsx files — exit 0. pnpm check:new-line-citations (ui) — 0 new citations.
  • Ablation via scripts/ablation-replace.mjs: setEmailDeliveryOff(config?.features?.emailDelivery === false); replaced by setEmailDeliveryOff(false); — 3 of the new tests fail (no-mail text, known/unknown parity, translated labels), 7 pass; restore verified blob == HEAD and git diff HEAD empty. (The three keep-old-text cases pass under the mutation by design.)

Verification

Derived gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 35 commands, all run, each exit 0, including node scripts/check-sdui-manifest.mjs and pnpm --dir ui exec vitest run scripts/ (170 files passed). --ran reconciliation: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN; pnpm check:changeset-fixed also run (outside the derivation), exit 0. CI type-check lanes are left to CI.

Acceptance notes

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (eslint on changed files; full run is CI's)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Utp6CYv3DdXJ81i2yQpGKm


Generated by Claude Code

…e deployment sends no mail (#30366)

Read features.emailDelivery from /auth/config; on an explicit false the
success screen says no reset link can be emailed here and to ask an
administrator (Setup → Users). Absent key or failed fetch keeps today's
text. The text names no address, so known and unknown addresses read the
same. New labels in all ten locale packs, wired through both forgot-password
pages.

Claude-Session: https://claude.ai/code/session_01Utp6CYv3DdXJ81i2yQpGKm
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Nov 5, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-bill · session_01Utp6CbPN5UYMnBaNJPJWfG

维护者速读

席位核对 — ACCEPT · head 28a6cc3

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 11:11
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 11:11
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 7b10a30 Nov 5, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-k9z05a branch November 5, 2026 11:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Forgot password page: read features.emailDelivery and say a reset link cannot be emailed here (ask an administrator) instead of saying it was sent

1 participant