fix: persist last selected org on the user#1745
Conversation
The active org lived only in the device-local showorg cookie, so a multi-org user signing in on a fresh browser landed in an arbitrary org (organization[0]). Store the selection on the user: change-org and invite acceptance persist it, the auth middleware falls back to it when no cookie is present, and removing a member from an org clears it so they return to their own org. No regression for existing users on deploy: - Accounts that were added to another org and carry a valid showorg cookie get lastSelectedOrgId seeded from that cookie by the middleware on their first request, keeping them in the org they were using. - Accounts living in their own original org (single member or with team members they added) have no showorg cookie; their value stays null and the middleware keeps resolving organization[0] - their own org - exactly as before. The field is set lazily the first time they make an explicit org choice (org selector or invite acceptance), which is the first moment it matters. Migration required: adds a new nullable column User.lastSelectedOrgId, so the schema change must be applied on deploy (prisma db push). Additive and nullable, safe for running instances. Tested manually end-to-end: org switch persists across sign-ins, invite acceptance seeds the value, and removal from the org resets the removed user back to their own org. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Contribution-checker quality warning Heuristics that flagged:
If this is a genuine contribution, please add detail to your PR description and tighten the diff scope before reviewers look at it. |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
| if (cookieOrg && !user.lastSelectedOrgId) { | ||
| await this._userService.updateLastSelectedOrg(user.id, cookieOrg.id); | ||
| } |
There was a problem hiding this comment.
Bug: The lastSelectedOrgId in the database is not updated from the showorg cookie if it already has a value, leading to desynchronization in multi-device scenarios.
Severity: LOW
Suggested Fix
Modify the condition in the authentication middleware to update user.lastSelectedOrgId whenever a valid cookieOrg is present and differs from the database value, not just when the database value is null. This ensures the database always reflects the most recent organization selected via a cookie-setting flow.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: apps/backend/src/services/auth/auth.middleware.ts#L97-L99
Potential issue: In the authentication middleware, the database field
`lastSelectedOrgId` is only updated from the `showorg` cookie if `lastSelectedOrgId` is
null. If a user logs in on a new device, the login flow sets a `showorg` cookie but does
not update the database. If `lastSelectedOrgId` was already set from a session on a
different device, it will not be updated to match the new cookie. This creates a
desynchronization. If the user later uses a third device without a cookie, the system
will fall back to the stale `lastSelectedOrgId` from the database, showing them an
organization they were not recently using.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Not a bug. Login never sets a showorg cookie — only the org selector and invite acceptance do, and as of this PR both of those persist lastSelectedOrgId in the same action, so the desync described here can't be created. The null-only condition is a one-time backfill for cookies that predate the column. Syncing the DB from any differing cookie would invert the semantics: a stale device would overwrite the user's latest explicit choice just by browsing. Per device the cookie wins for that session; the DB keeps the last explicit selection as the default for cookie-less sign-ins.
What kind of change does this PR introduce?
Bug fix.
Why was this change needed?
The active org lived only in the device-local
showorgcookie, so a multi-org user signing in on a fresh browser landed in an arbitrary org (organization[0]). In practice: a member added to another org who signs in on a new device lands in their own org instead of the one they work in, with no persisted memory of their choice.This PR stores the selection on the
Userrecord:POST /user/change-org(the org selector) persists the choice after validating membership.showorgcookie the auth flow already sets).lastSelectedOrgId→organization[0].No regression for existing users on deploy:
showorgcookie getlastSelectedOrgIdlazily seeded from the cookie on their first request — they stay in the org they were using.nulland the middleware keeps resolvingorganization[0]— their own org — exactly as before. The field fills in the first time they make an explicit org choice, which is the first moment it matters.Migration required: adds a new nullable
User.lastSelectedOrgIdcolumn (applied viaprisma db pushon deploy). Additive and nullable — safe for running instances and old clients during rollout.Other information:
Tested manually end-to-end: org switch persists across sign-ins, invite acceptance seeds the value, and removal from the org resets the removed user back to their own org. The role-reassignment feature PR is stacked on top of this branch, since transferring a role between orgs is what surfaced this pre-existing gap.
Checklist:
🤖 Generated with Claude Code