Skip to main content

ADR-0014: Multi-Account Profile Switcher

  • Status: Implemented
  • Date: 2026-06-19

Context

Enterprise users frequently operate across multiple user identities (e.g., personal vs. corporate accounts, administrative vs. standard accounts, or acting on behalf of different organizations/clients).

Historically, switching identities required logging out completely and signing in again, which creates friction and disrupts active work. We need a secure, developer-friendly mechanism to maintain multiple authenticated profiles within the same browser session manager, allowing the user to context-switch dynamically in the UI or remove individual profiles (in-session individual logout) without losing session context for other active accounts.

Additionally, we must establish a clear distinction between Multi-Account Profile Switching and User Impersonation, which are often conflated but have different authentication constraints, security boundaries, and audit trail requirements.

Decision

Implement an internalized Multi-Account Profile Switcher in the core framework BFF and UI shell, and explicitly define the boundary separating it from administrative impersonation.

1. The Multi-Account Session Model

The browser session cookie contains only an opaque session manager ID. The actual account metadata resides server-side in a signed and encrypted session record containing:

  • active_account_id: Pointer to the currently active profile.
  • accounts: A dictionary of authenticated sub-sessions representing verified profiles.

2. Feature Gating

To protect applications that do not require multi-tenancy, the feature is disabled by default:

  • ENABLE_PROFILE_SWITCHER=False (default).
  • When disabled, any profile switcher or merge endpoints (such as POST /auth/switch-profile or POST /auth/remove-profile) reject immediately with 403 Forbidden (PermissionDeniedException), and switcher menus are hidden in the UI.

3. Clear Distinction from Impersonation

The framework separates profile switching from user impersonation along the following dimensions:

DimensionMulti-Account Profile SwitcherUser Impersonation ("Run As" / "Sudo")
Authentication ProofMandatory. The user must have successfully authenticated (using passwords, OIDC callback, or magic links) for every profile in the session.Administrative delegation. An administrator or privileged user assumes another identity without knowing or verifying their credentials.
Session BindingAll profiles are tied under a single server-side session manager, sharing a cookie and an absolute timeout.A separate temporary impersonation token or sub-session is issued, scoped to the admin session and usually short-lived.
Audit TrailsLogged under the active profile's own user identity as the direct actor of the transaction.Dual-actor logs. Audit logs must log both the impersonator (actor/operator) and the target user (subject/context) for non-repudiation.
Security ScopeFull access to the selected account's data, subject to standard ACLs/RBAC.Scoped privileges. Security overrides prevent the impersonator from performing sensitive operations (e.g. changing passwords, configuring MFA, or signing documents).

4. Same-Domain Custom Login Integration

For deployments acting as OIDC Identity Providers (like M-OIDC), the backend handles client prompts:

  • When a client sends prompt=select_account or an intervention is needed, the authorization server redirects to the custom login route with the intervention context pending_action: "select_account".
  • The custom page is responsible for rendering the custom chooser UI, while the framework exposes /auth/switchable-users and /auth/switch-profile APIs to execute the selections securely.

5. In-Session Individual Profile Removal (Logout)

Implement Pattern 1: In-Session Individual Profile Removal:

  • Users can log out of a single profile without destroying the entire session.
  • Active Profile Swap: If the removed profile is the active one and other profiles remain, the system automatically redirects and switches context to another remaining profile in the session.
  • Session Termination: If the last profile is removed, the server destroys the session manager record entirely and issues a cookie-clearing header.
  • Inactive Profile Invalidation: If the removed profile was inactive (in the background), the React Query cache key ["switchable-profiles"] is invalidated in the UI to update the list dynamically without reloading the page.

Consequences

Positive

  • Improved UX: Frictionless context switching and account adding.
  • Security Boundaries: In-session individual profile removal ensures users can safely clean up specific profiles from public/shared devices.
  • Audit Compliance: Because profile switching requires complete authentication, audit trails remain simple and map 1:1 with the active account context, preventing the tracking complexity associated with impersonation.

Negative

  • Session Lifecycle Complexity: Session stores must handle dictionary-style schemas and check off sub-sessions during idle/absolute timeouts.
  • CSRF & State Token Overhead: Adding an account via external OIDC callbacks requires strict validation of session-bound state parameters to prevent malicious profile insertion.