Skip to content

fix: persist last selected org on the user#1745

Open
giladresisi wants to merge 1 commit into
mainfrom
feat/persist-last-selected-org
Open

fix: persist last selected org on the user#1745
giladresisi wants to merge 1 commit into
mainfrom
feat/persist-last-selected-org

Conversation

@giladresisi

Copy link
Copy Markdown
Collaborator

What kind of change does this PR introduce?

Bug fix.

Why was this change needed?

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]). 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 User record:

  • POST /user/change-org (the org selector) persists the choice after validating membership.
  • Invite acceptance persists the joined org (matching the showorg cookie the auth flow already sets).
  • The auth middleware resolves the org as: cookie → persisted lastSelectedOrgIdorganization[0].
  • Removing a member from an org clears their persisted value if it pointed at that org, so they fall back to their own org.

No regression for existing users on deploy:

  • Accounts added to another org that carry a valid showorg cookie get lastSelectedOrgId lazily seeded from the cookie on their first request — they stay in the org they were using.
  • Accounts in their own original org (single member, or with team members they added) have no cookie; their value stays null and the middleware keeps resolving organization[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.lastSelectedOrgId column (applied via prisma db push on 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:

  • I have read the CONTRIBUTING guide.
  • I have signed the Contributor License Agreement (CLA) (ICLA for individuals, CCLA for entities).
  • I confirm I have not used AI to submit this PR or generate code for it.
  • I checked that there were no similar issues or PRs already open for this.
  • This PR fixes just ONE issue

🤖 Generated with Claude Code

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>
@postiz-contribution postiz-contribution Bot added the contribution:approved Approved contributor label Jul 19, 2026
@postiz-contribution

Copy link
Copy Markdown

Contribution-checker quality warning
Heuristic score: 20/100 (low). This is a non-blocking warning surfaced by the project's quality settings.

Heuristics that flagged:

  • Wall-of-text PR body: 2652 chars (>2500)
  • Excessive inline code references: 13 inline refs (>3)
  • AI watermark phrase: Matched: "Generated with Claude Code"
  • Commit message too long: Longest: 1476 chars

If this is a genuine contribution, please add detail to your PR description and tighten the diff scope before reviewers look at it.

@postiz-agent

postiz-agent Bot commented Jul 19, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Comment on lines +97 to +99
if (cookieOrg && !user.lastSelectedOrgId) {
await this._userService.updateLastSelectedOrg(user.id, cookieOrg.id);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution:approved Approved contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant