Treat a chosen account as a request selector. On every protected operation, the server must identify the authenticated person, verify active membership in that account, calculate its current capabilities there, and check the requested resource's owner. Switching the menu label, changing a URL segment or issuing a new token cannot transfer rights from one account to another. The answer changes if accounts use separate identity providers or home regions: a switch may require a new account-specific authentication flow, but it still cannot skip live membership and resource authorization. The OWASP Authorization Cheat Sheet calls for a check on every request; the Multi Tenant Security Cheat Sheet adds the cross-tenant boundary that makes this especially important in SaaS.
This guide uses a two-account case to build an account-switching review artifact. It covers browser and API behavior as well as delayed work and cached output. A clean UI demonstration alone is insufficient evidence.
Illustrative case: one subject, different jobs
Illustrative case, not an observed vulnerability: Ravi is a security administrator in Northstar Labs and a read-only executive in Solace Health. He has a valid browser session and switches from Northstar to Solace. The client writes activeAccount=solace to local storage and sends X-Account-ID: solace to the API. A report endpoint accepts a report ID and sends a cached PDF. An export job started under Northstar may finish after Ravi switches the browser to Solace.
The intended rules are specific. Ravi can read technical findings in Northstar under his security role. In Solace he can see only an approved executive projection, not source or exploit detail. An export requested under Northstar must remain bound to the Northstar membership, resource and authorization state at each later delivery step; the browser's current selection does not authorize it. Neither account's role is a global Ravi role.
For the direct PDF path, OWASP's API1:2023 broken object authorization guidance notes that changing an object ID can expose another object's data if ownership is not checked. A random report ID is still an identifier, not a permission. The same applies when the ID is in a route, header, cursor, queue message or presigned-download request.
Write the switch invariant before the code review
Use one sentence as the implementation contract:
For every operation
Oon resourceR, the server derives a validated subjectS, resolves the selected accountA, checks current active membership ofSinA, checks that membership's permission forO, and verifies thatRand every relevant parent belong toAbefore releasing data or changing state.
The client may suggest A; the server must not use that suggestion as authority. A session or token can carry an account claim for efficient routing, but an old claim cannot substitute for current membership. If an operation has no specific resource, still check the selected account and action. If it returns a list, the query and every resulting projection must stay inside the account. For a batch, apply the check to every member, not just the first. A failed or ambiguous owner lookup denies the operation.
Two tabs expose a bug the account menu hides
Use Ravi's different roles to create a synthetic timing sequence:
T0 Tab1 selects Northstar; request Q1 starts for technical report N-7
T1 Tab1 switches to Solace; view generation changes from 8 to 9
T2 Solace summary S-4 arrives and renders
T3 Slow Northstar response Q1 arrives
T4 Tab2, still in Northstar, requests N-7 legitimately
Q1 was authorized for Northstar when requested. Rendering it under Solace's heading can still expose the wrong account's material to someone viewing or sharing that screen. This is a client context-integrity problem in addition to server authorization. It is not evidence that Solace's server authorized Northstar data.
A small client-side pattern illustrates the intended behavior:
captured = {account: selectedAccount, generation: viewGeneration}
response = await fetchReport(captured.account, reportId)
if captured.account != selectedAccount: discard response
if captured.generation != viewGeneration: discard response
render(response)
This is unexecuted pseudocode, not a complete frontend security mechanism. Canceling old requests helps efficiency but cannot replace checking a response that already completed. Clearing account-specific state at the switch prevents an old card from lingering while the new request loads. Partition browser caches by canonical account, resource and authorized projection; include principal or policy identity when two viewers can receive different fields. Clear or invalidate protected entries on logout and relevant permission changes. An account-only cache key can still serve a prior administrator's projection to a newly signed-in reader.
Make every request carry an unambiguous context
Tab2 should not silently become Solace because Tab1 changed a mutable server-wide “active account” field. A design can use account-bound sessions or explicit per-request selectors with current checks. Either way, the server needs an unambiguous account for the operation, current membership and action rights there, and the resource's persisted owner.
subject = validated_session.subject
account = canonical(request.account_selector)
membership = current_membership(subject, account)
require membership permits requested_action
report = load_report_in_account(account, request.report_id)
return projection_allowed_by(membership, report)
The browser generation guard does not authorize this request. The server checks do not guarantee the browser displays it under the correct heading. Both boundaries matter, and their evidence should be reported separately.
For Ravi, Northstar can return the approved technical section of N-7. Solace can return S-4's executive summary only. A role cached as “security_admin” on Ravi's global identity would incorrectly borrow Northstar's power for Solace. Recompute or retrieve rights in the selected membership, not as a union across accounts.
Keep delayed work attached to its original account
The Northstar export started before T1 remains a Northstar job. Switching the current tab does not rewrite its tenant, recipient or grant. A notification should identify its account context and lead through a fresh authorized delivery decision. If Northstar removes Ravi while it runs, his remaining Solace membership cannot authorize the result.
If accounts require different IdPs or assurance, switching may start a target-account sign-in before any protected data is shown. If membership lookup fails, deny the protected operation rather than using a prior account's cached role. OWASP's multi-tenant guidance supplies the broader isolation principles; the two-tab sequence is an original review fixture that makes a subtle display and cache failure inspectable.
A filled request-context decision table
| Requested operation | Selected account | Current membership | Resource owner | Decision and reason |
|---|---|---|---|---|
Read technical report N-7 |
Northstar | Security admin | Northstar | Allow authorized technical projection |
Read technical report N-7 |
Solace | Executive | Northstar | Deny: report owner differs |
Read Solace executive summary S-4 |
Solace | Executive | Solace | Allow executive projection only |
Read Solace source section S-4/source |
Solace | Executive | Solace | Deny: no technical-section capability |
| Download old Northstar export | Solace browser tab | Northstar membership now removed | Northstar | Deny Ravi's delivery despite export having completed |
The last row requires a product decision about who, if anyone, may retrieve a completed export after its requester is removed. The narrow outcome is that Ravi cannot. Do not infer that deleting a browser link removes a previously downloaded copy.
When the boundary is unclear
If a legacy handler accepts a report ID but the record has no trustworthy owner, quarantine that route or require a migration that establishes ownership; guessing from the URL is unsafe. If the membership service is unavailable, deny protected operations and show a retryable error rather than falling back to a token's old account claim. If a cache cannot be partitioned by account and projection, bypass it for protected content until its keys and invalidation are corrected. If an identity provider does not give a stable subject, stop automatic cross-account linking and resolve the identity with an explicit, audited process.
Bind navigation and mutations to the same selected account
The slow-response example concerns display, but a stale form can create a more consequential confusion. Suppose Ravi opens a Northstar settings form, switches the visible account to Solace, and then submits the old form. The application should preserve the original operation context or explicitly discard the stale form. It should not relabel the form as Solace while sending Northstar values under a newly selected account.
The server still performs its own current membership, action, and resource checks. Those checks may correctly authorize Ravi in Northstar, yet the user experience can cause him to act on the wrong customer's account. Account isolation and user intent are related but distinct review questions. The request should identify its intended account unambiguously, and the confirmation screen should display that same account before a material change.
For the synthetic application, attach the canonical account identity to the form's loaded state and treat a switch as a change of context. If the user wants to continue editing Northstar, they can return to that account or use the still-correct second tab. If they intend a Solace change, load Solace's authorized state and projection afresh. Do not copy hidden technical fields from Northstar into Solace's executive-only form.
Give deep links an explicit account context
An emailed export link may open while the browser currently displays another account. The link should locate the resource and its account, then require fresh authorization for that resource. It should not assume the browser's selected account owns the job, nor should it silently change a global session role to make the link work.
For Ravi's Northstar export, the delivery page can identify the required context under the product's disclosure policy and offer a deliberate switch or sign-in flow. After that flow, the server rechecks Northstar membership and the export's persisted owner. Solace membership cannot substitute. If the link is an independently usable bearer capability, its different lifetime and revocation boundary must be reviewed separately; the account menu cannot enforce it.
A useful review record distinguishes the link's locator, the account-selection step, the authenticated identity, and the final resource decision. This makes a failure easy to locate. A wrong heading belongs to client context handling; an unauthorized PDF belongs to the delivery path; an old export available after removal belongs to revocation or delegation policy.
Exercise the switch under ordinary user behavior
The most informative manual sequence uses two open tabs, a delayed read, a saved link, and a role difference between accounts. Record what each tab displays and which account each protected request actually names. The same identity should be permitted to do different things in Northstar and Solace without either grant becoming global.
Include a logout and sign-in as another authorized synthetic user on the same device. The earlier cache correction matters here: an account-only cache key can retain a technical projection for a person who should receive only an executive summary. Inspect the response and the rendered fields separately so the reviewer can tell whether the server released too much or the client reused an older authorized response.
These are proposed controlled review steps, not observations from the example. Their value is that they connect realistic navigation to the stated invariant. They reveal problems that a single immediate switch between two identical administrator accounts would be unlikely to expose.
Pick one direct object read and one delayed export. Fill the five decision rows with your real account IDs, roles, owner checks and denial evidence. For the related identity boundary, use the email-domain membership guide; for stale sessions after offboarding, continue with session revocation.