Skip to main content

Multi-Tenant Access Control

Overview

This guide explains how to implement multi-tenant access control in Medplum projects using AccessPolicy and Organization FHIR resources. This approach allows you to restrict access to resources based on organizational membership, making it ideal for scenarios where you need to separate data access between different organizations or practices within the same project.

Architecture

The multi-tenant access control system is built on these key components:

  1. An AccessPolicy that defines access rules with parameterized variables
  2. Organization resources that represent different tenants
  3. Resources (like Practitioner, Patient, Questionnaire) that are associated with specific organizations
  4. The meta.accounts field that links resources to organizations

Visual Architecture

A multi-tenant system where one AccessPolicy controls access for multiple organizations and their associated resources (practitioners, questionnaires, and patients)

Implementation Guide

1. Create the Access Policy

In AccessPolicy we use the parameterized variable %current_organization in the compartment section and then we use %current_organization again for Criteria-based Access Control.

{
"resourceType": "AccessPolicy",
"id": "multi-tenant-org-policy",
"name": "Multi-Tenant Organization Access Policy",
"compartment": {
"reference": "%current_organization"
},
"resource": [
{
"resourceType": "ValueSet"
},
{
"resourceType": "CodeSet"
},
{
"resourceType": "Organization",
"criteria": "Organization?_id=%current_organization",
"readonly": true
},
{
"resourceType": "Practitioner",
"criteria": "Practitioner?organization=%current_organization"
},
{
"resourceType": "Questionnaire",
"criteria": "Questionnaire?_compartment=Organization/%current_organization"
},
{
"resourceType": "Patient",
"criteria": "Patient?organization=%current_organization",
"readonly": true
}
]
}

2. Create the Organizations

Create separate organizations for each tenant:

{
"resourceType": "Organization",
"name": "Organization A"
}
{
"resourceType": "Organization",
"name": "Organization B"
}

3. Associate Resources with Organizations

When creating resources, associate them with their respective organizations using the meta.accounts field. Here are examples for Questionnaire resources:

Questionnaire for Organization A

{
"resourceType": "Questionnaire",
"meta": {
"accounts": [
{
"reference": "Organization/{{organization_a}}"
}
]
},
"title": "Experience Rating Questionnaire",
"status": "active",
"item": [
{
"linkId": "1",
"text": "How would you rate your overall experience?",
"type": "choice",
"answerOption": [
{
"valueCoding": {
"system": "http://example.org/rating",
"code": "5",
"display": "Excellent"
}
}
]
}
]
}

Questionnaire for Organization B

{
"resourceType": "Questionnaire",
"meta": {
"accounts": [
{
"reference": "Organization/{{organization_b}}"
}
]
},
"title": "Experience Rating Questionnaire",
"status": "active",
"item": [
{
"linkId": "1",
"text": "How would you rate your overall experience?",
"type": "choice",
"answerOption": [
{
"valueCoding": {
"system": "http://example.org/rating",
"code": "5",
"display": "Excellent"
}
}
]
}
]
}

Create Users and attach AccessPolicy

First create a Practitioner User associated with Organization A via the /admin/invite endpoint

{
"resourceType": "Practitioner",
"firstName": "Practitioner",
"lastName": "A.1",
"email": "practitioner.a.1@example.com",
"sendEmail": "false",
"password": "foobar",
"membership": {
"access": [
{
"policy": {
"reference": "AccessPolicy/{{access_policy}}"
},
"parameter": [
{
"name": "current_organization",
"valueReference": {
"reference": "Organization/{{organization_a}}"
}
}
]
}
]
}
}

Then create a Practitioner resource associated with Organization B

{
"resourceType": "Practitioner",
"firstName": "Practitioner",
"lastName": "B.1",
"email": "practitioner.b.1@example.com",
"sendEmail": "false",
"password": "foobar",
"membership": {
"access": [
{
"policy": {
"reference": "AccessPolicy/{{access_policy}}"
},

"parameter": [
{
"name": "current_organization",
"valueReference": {
"reference": "Organization/{{organization_b}}"
}
}
]
}
]
}
}

Now Practitioners should only be able to read or write resources mentioned in the AccessPolicy that belong to an Organization they are a member of.

Postman example

To see a full example of this set up using cURL, you can visit this postman collection - Multi-Tenancy Project.