Repository navigation
fix(console): after a sign-out, the next person to sign in lands on their own home, not the previous person's page - #30490
Merged
Conversation
…item 4) After a sign-out, LoginRedirect kept the page the previous person had open as `?redirect=`, so the next person to sign in landed there. The auth provider now records that the session was ended by signOut() and lowers the record once any session exists again; LoginRedirect sends that case to /login with no return link. An expiry or revocation keeps the path. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y
… false (#30125 item 4) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y
…xt commit) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y
…mit) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y
…effect (#30125 item 4) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y
… commit) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y
…textValue member (#30125 item 4) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y
Contributor
Author
|
domain:ui#5 · os-sales · session_01L6bhgVwQZbHRRM9CaXeKif Contract reviewServed-tier: Reviewed 2026-11-05T13:30Z. Face: the ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
Contributor
Author
|
domain:ui#5 · os-sales · session_01L6bhgVwQZbHRRM9CaXeKif 维护者速读
席位核对 — ACCEPT · domain:ui#5 · head
|
This was referenced Nov 5, 2026
This was referenced Nov 5, 2026
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.
domain:ui#5 · os-sales · session_01L6bhgUtwT9qwYbCjRDVu5Y
Requested by Sales · project thread
Part of #30125 (item 4 fixed here; item 3 measured and not reproduced on main, no change; items 1, 2 and 5 stay on the card).
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
?redirect=回到上一个人打开的页面,下一个人登录后落到自己的首页。会话过期或被吊销(不是主动退出)时仍保留原路径,同一个人重新登录后回到原处。ui/packages/auth/src/AuthProvider.tsx(sessionEndedBySignOut的升起与落下)、ui/apps/console/src/components/ProtectedRoute.tsx的LoginRedirect、测试文件ProtectedRoute.signOutListState-29174.test.tsx新增的 objectstack#30125 describe。pnpm --dir ui exec vitest run apps/console/src/components/ProtectedRoute.signOutListState-29174.test.tsx,预期 7 个用例全绿。What changes
Card #30125, item 4: after a sign-out, the next person lands on the previous person's page.
@object-ui/auth:AuthContextValue.sessionEndedBySignOut(new, boolean).AuthProvider.signOut()raises it in the same batch that clearsuser/session, so the first render without a session already reads it; it is lowered during render as soon as a session exists again, whoever it belongs to. A session that ends any other way (loadSessionre-resolving to nothing: expiry, revocation, a sign-out in another tab) leaves itfalse. The outside-provider default inuseAuth.tsanswersfalse.@object-ui/console:LoginRedirectsends the visitor to/loginwith no?redirect=whensessionEndedBySignOutis true. Otherwise it is unchanged: the path and params are kept, listuf_*state stripped (Console sign-out: the login page's redirect carries the previous user's list filter, search and sort, so the next person who signs in on that browser opens their list state #29174).AppHeader,UserMenu,RemediationOverlay) go throughAuthProvider.signOut(), so the change is at that one source;AppHeader.tsxis not touched..changeset/30125-sign-out-no-return-link.md,@object-ui/authand@object-ui/consolepatch.Branch: the harness-assigned
claude/project-thread-guv7bd, notclaude/issue-30125-*; the card is #30125.Tests
In the existing harness file
ProtectedRoute.signOutListState-29174.test.tsx(realAuthProviderover a scripted client, realProtectedRoute, real ConsoleLoginPage), a new top-level describe for #30125, 3 cases:/login, noredirect; B signs in and lands on/.refreshSessionre-resolves to no session); the login URL keeps the path; A signs back in and lands on it.The #29174 block's first case now ends A's session by expiry instead of by sign-out: an explicit sign-out carries no return link at all, so the list-state stripping it pins is only reachable on the expiry path. Its assertions are otherwise unchanged (moved pin, declared).
Acceptance notes
os start(Mode: production) onexamples/app-todo, console built bypnpm console:buildat989ae42d, Chromium 1440x900, zh-CN, Asia/Shanghai, a freshly registered first administrator. The dialog opened 0.2 to 0.3 s after the first entry into any/apps/...route (Setup), before any action was possible; after it was answered, inviting a member raised the invitation toast and no dialog opened over it. The prompt is mounted only under/apps/:appName/*and asks once per org and user, so on main it cannot first appear after an in-app action. Residual, not measured: an action OUTSIDE/appsthat raises a toast and then navigates into an app within the toast's 4 s could still open the dialog over it; no such path was found in the first-run flow.AuthContextValuegains a required member. Code that builds its own value of that type (a test double typed asAuthContextValue) must add it; in this tree the only such builders areuseAuth.ts(updated) andinbox-arrival-preview.tsx, which casts throughunknown. DeclaredClause-②: yes (widening)for this, as PR fix(auth): a user with two-factor sign-in on is asked for their code after the password #29338 did for itsAuthContextValue/AuthClientmembers.Verification
Tests, typecheck and the derived gates ran on
aa800e72(the fix) or earlier as noted; the head adds evidence commits and their removals, the tree is byte-identical toaa800e72(git diff aa800e72 HEADis empty). Base989ae42d.Live rig and screenshots (production mode, zh-CN, 1440 wide)
pnpm build+pnpm console:build, thenobjectstack start -p PORT --home TMP --database file:TMPinexamples/app-todo(banner:Mode: production). First administrator registered on/register; a member invited from Setup, Invitations, then signed up and accepted through the auth API.Item 4, before (main
989ae42d): the administrator signs out of/apps/com.objectstack.setup/sys_user; the login page is/login?redirect=%2Fapps%2Fcom.objectstack.setup%2Fsys_user; the member signs in and lands on that page: 你没有访问此应用的权限.Item 4, after (console rebuilt at
aa800e72): the login page is/login; the member lands on/, which opens the member's own default app.Item 3 (main
989ae42d): the dialog at first entry into Setup, and the invitation toast uncovered after it was answered.Tests, ablation, typecheck, lint
pnpm --dir ui exec vitest run --maxWorkers=3 packages/auth/ apps/console/ packages/app-shell/src/providers/__tests__/MetadataProvider.crossPrincipalSeed.test.tsxonaa800e72: Test Files 341 passed (341), Tests 3315 passed (3315), exit 0.scripts/ablation-replace.mjs, anchor x1 to x0):LoginRedirect's sign-out branch disabled: 2 failed / 5 passed (the sign-out case and the forgotten-sign-out case). Restored, blob equals HEAD,git diff HEADempty.aa800e72: the flag's reset inAuthProviderdisabled: 1 failed / 6 passed (the forgotten-sign-out case). Restored, blob equals HEAD.pnpm --filter @object-ui/auth run type-check,@object-ui/app-shell,@object-ui/console(after rebuilding@object-ui/auth): each exit 0.pnpm --dir ui run check:spec-symbols: exit 0.AuthProvider.tsxlines this PR does not change.Derived gates (35/35 exit 0)
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 35 families; the list is identical at the final head. Every one ran and exited 0; the sweep ran while the head moved from0ddd2bd3to7a94e786, and the later source commits change only lines insideui/packages/auth/srcthat no family reads.--ranreconciliation: 35 derived, 35 run, 0 NOT-MEASURED (a derived zero, every line carries an exit code). Alsopnpm --dir ui run check:new-line-citationsandcheck:control-bytes: exit 0. CI runs the rest.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgUtwT9qwYbCjRDVu5Y