Building a Tenant Selector | Medplum
On this page
This document is a follow-up to Multi-Tenant Access Control.
In an MSO Access Control model, a single practitioner often has access to multiple tenants (for example, multiple Organization clinics) within the same Medplum Project. After login, most applications need an answer to:
- Which tenant context is the user working in right now?
- When should the UI show a consolidated cross-tenant view vs. a tenant-scoped view?
This guide describes three common approaches and how to implement a tenant selector when you need one.
At a Glance
Use the decision tree to pick the option that fits your product, then jump to that section for implementation details.
| Option 1: Strict Isolation | Option 2: Single Account + Selector | Option 3: Unified View | |
|---|---|---|---|
| API-level tenant isolation | Strongest (one tenant per token) | Allowed tenants enforced; UI scopes within them | None beyond the project |
| Consolidated cross-tenant views | Difficult | Supported (when UI omits the filter) | Default |
| Tenant-scoped PHI screens | Yes (always) | Yes (when UI applies the filter) | No |
| Login / membership model | One membership per tenant per user | One membership covering multiple tenants | One membership covering all tenants |
| Tenant switching | Re-auth or saved-login swap | UI control (no re-auth) | N/A |
| Implementation complexity | Highest | Medium | Lowest |
Key Concept: Allowed Tenants vs Active Tenant
- Allowed tenants: The set of tenant references the user is permitted to access (enforced by
ProjectMembership+AccessPolicy). - Active tenant: A single tenant reference that your application uses as the default filter for PHI (a UI/application concept).
When you build a "tenant selector", you're choosing and persisting the active tenant.
Option 1: Strict Isolation (Separate Accounts / Separate Projects)
Goal: Hard isolation of PHI at the API level. At any given time, the "active tenant" is the only "allowed tenant" for the user.
What This Looks Like
- The user has separate logins per tenant.
- Each login/profile has a
ProjectMembershipthat only includes access to one tenant.
Each login is bound to a single tenant. Switching tenants requires switching the active login.
Switching the Active ProjectMembership
In Medplum, the "active membership" is not a client-side flag you can flip on an existing token. The OAuth login flow selects a membership, and the issued access token is bound to that membership.
Practically, "switching memberships" means one of:
- Switching to another existing login (you already authenticated previously and have a saved token pair).
- Starting a new login and selecting a different membership during the login handshake.
How Membership Selection Works in the Login Flow
- If a user only has one membership, Medplum selects it automatically.
- If a user has multiple memberships, the client displays a "choose profile" screen and posts the selected membership ID to
POST /auth/profile.
How to Configure a User with Multiple Memberships
You need to invite the user once per membership you want in the project (for example one membership per tenant in a strict-isolation MSO model). See the Invite User endpoint.
Creating the First ProjectMembership
// First membership invite for this user in this project.
await medplum.post(`admin/projects/${projectId}/invite`, {
resourceType: 'Practitioner',
firstName: 'George',
lastName: 'Washington',
email: 'dr.gw@example.gov',
sendEmail: false,
membership: {
access: [
{
policy: { reference: 'AccessPolicy/mso-tenant-policy' },
parameter: [{ name: 'organization', valueReference: { reference: 'Organization/clinic-a' } }],
},
],
},
});
Creating an Additional ProjectMembership
// Second membership invite for the same user in the same project.
await medplum.post(`admin/projects/${projectId}/invite`, {
resourceType: 'Practitioner',
firstName: 'George',
lastName: 'Washington',
email: 'dr.gw@example.gov',
sendEmail: false,
forceNewMembership: true,
membership: {
access: [
{
policy: { reference: 'AccessPolicy/mso-tenant-policy' },
parameter: [{ name: 'organization', valueReference: { reference: 'Organization/clinic-b' } }],
},
],
},
});
Option 2: Single Account, Multi-Tenant Access + UI-Level Tenant Context
Goal: One login (ProjectMembership) can access one or multiple defined tenants, but the FE application chooses an "active tenant".
Granting Access to Multiple Tenants
If your AccessPolicy uses %organization:
{
"resourceType": "ProjectMembership",
"access": [
{
"policy": { "reference": "AccessPolicy/mso-policy" },
"parameter": [
{ "name": "organization", "valueReference": { "reference": "Organization/clinic-a" } },
{ "name": "organization", "valueReference": { "reference": "Organization/clinic-b" } }
]
}
]
}
With that membership, an access policy criteria like Patient?_compartment=%organization can allow access to patients across both tenants.
Option 3: Unified View (No Tenant Context)
Goal: Treat the project as the only boundary; the user always sees everything they're allowed to see.
When to Choose It
- Your product model is truly unified (e.g., one clinical ops team across all clinics).
- Your contracts and workflows do not require tenant-scoped PHI screens.