Multi-Tenancy with Fine-Grained Access Control
Use case
A SaaS healthcare platform hosts multiple independent clinics on a single Fire Arrow Server instance. Each clinic has its own patients, practitioners, and clinical data. The platform must guarantee that Clinic A's staff never sees Clinic B's patients - not through a bug, not through a search, not through a GraphQL query. Within each clinic, different staff roles need different access levels: doctors need full clinical access, nurses need read-only clinical access, and IT administrators should manage organizational resources without ever touching patient data. On top of that, the platform's own support team needs to be able to assist any clinic when asked, and a specialist at one clinic occasionally consults on a patient from another clinic.
Other scenarios this recipe applies to
- Hospital network: A central administration oversees multiple department-level organizations (cardiology, radiology, emergency). Department heads need cross-department visibility, but staff members should only see data within their department.
- Research consortium: Multiple research institutions share a FHIR server. Each institution's data must be isolated, but a designated study coordinator needs read access across all sites.
What you will build
Prerequisites
- Fire Arrow Server is running with authentication enabled.
- You have administrative access to create Organizations, Practitioners, and PractitionerRoles.
- You are familiar with authorization concepts.
Understanding the building blocks
Before configuring anything, it helps to understand why each piece exists and what problem it solves. Multi-tenancy in Fire Arrow Server is not a single feature - it is the result of several features working together. This section explains each one.
Why organization-based isolation?
FHIR does not have a built-in "tenant" concept. Some systems solve multi-tenancy by hard-partitioning data into separate databases or URL paths. This is simple but rigid: a practitioner cannot easily belong to two tenants, patients cannot be transferred between tenants, and adding a new tenant requires reconfiguration.
Fire Arrow Server takes a different approach. It uses the standard FHIR Organization resource combined with Patient.managingOrganization and PractitionerRole to create natural data boundaries. Each clinic is an Organization. Each patient belongs to a clinic via managingOrganization. Each practitioner belongs to one or more clinics via PractitionerRole. The authorization system uses these relationships to determine who can see what.
This approach is more flexible: a practitioner can work at two clinics, a patient can be transferred by changing their managingOrganization, and a new clinic can be added by creating a new Organization - no server restart needed.
How LegitimateInterest creates data boundaries
The LegitimateInterest validator is the core of this architecture. Here is how it works, step by step:
- Every patient belongs to an organization via
Patient.managingOrganization. - Every practitioner belongs to one or more organizations via active
PractitionerRoleresources. - When a practitioner makes a request, LegitimateInterest collects all organizations from their PractitionerRoles. This is their "organizational scope."
- The validator then grants access to all patients whose
managingOrganizationmatches one of those organizations, and to clinical data (Observations, Conditions, Encounters, etc.) that belongs to those patients.
The result: a doctor at Clinic A sees only Clinic A's patients - automatically, without any custom query parameters or manual filtering. If the doctor also has a PractitionerRole at Clinic B, they see both clinics' data. The boundary is always enforced by the server, never by the client.
For patient clients, LegitimateInterest works in the other direction: a patient can see practitioners and organizational resources (devices, locations, healthcare services) belonging to their managing organization. They cannot see resources from other organizations.
How role codes create access tiers
Within a single organization, not everyone needs the same access. A doctor needs to read and write clinical data. A nurse may only need read access. An IT administrator needs to manage devices and practitioner accounts but should never see patient records.
FHIR models this with PractitionerRole.code. Each PractitionerRole carries a code that describes the practitioner's function (doctor, nurse, ict, etc.). Authorization rules can specify practitioner-role-system and practitioner-role-code to restrict a rule to practitioners with a specific role code. A rule with practitioner-role-code: doctor is evaluated only when the practitioner has a PractitionerRole with that code in one of their organizations.
This means you can create multiple LegitimateInterest rules for the same resource type and operation, each targeting a different role code with different permissions.
How role inheritance enables hierarchical access
Organizations can form hierarchies using the Organization.partOf reference. A hospital might have:
Regional Health Authority
└── City General Hospital
├── Cardiology Department
└── Radiology Department
By default (with role-inheritance-levels: 0), a practitioner only sees data in the organizations where they have a direct PractitionerRole. A practitioner at the Regional Health Authority level cannot see data in City General Hospital.
When you configure role-inheritance-levels: N, a practitioner's access cascades downward N levels through the hierarchy. With role-inheritance-levels: 2, a practitioner at the Regional Health Authority gains access to City General Hospital (1 level down), Cardiology (2 levels down), and Radiology (2 levels down).
Crucially, inheritance is downward only. A staff member at the Cardiology Department never gains access to the Regional Health Authority's data, no matter how many inheritance levels are configured. This ensures that hierarchical access is always a privilege of the parent, not the child.
For the SaaS platform scenario, this means a support team member with a PractitionerRole at the root "HealthTech Platform" organization gains access to all clinics, while clinic staff only see their own clinic.
How additive rules differ from Fire Arrow Core
This is a key architectural difference between Fire Arrow Server and Fire Arrow Core.
In Fire Arrow Core, authorization rules are exclusive: the first matching rule wins. If a practitioner matches a LegitimateInterest rule, that rule alone determines their access. This is simpler but less flexible.
In Fire Arrow Server, rules are additive: a practitioner's access is the union of all matching rules. This means you can combine:
- A LegitimateInterest rule for intra-organizational access (the doctor sees all patients in their clinic).
- A CareTeam rule for cross-organizational access (the same doctor sees a specific patient from another clinic because they are on that patient's CareTeam).
The practitioner gets both scopes of access. Neither rule "overrides" the other - the results are merged.
This additive behavior is essential for realistic healthcare scenarios. A doctor should not lose their organization-wide access just because they are also on a CareTeam, and vice versa.
Where CareTeam fits in
LegitimateInterest handles the 90% case: practitioners see data within their own organization. But healthcare does not always respect organizational boundaries. A specialist at Clinic B may need to consult on a patient from Clinic A. A home care nurse from an external agency needs to access a discharged patient's medication list.
The CareTeam validator handles this. A CareTeam resource lists the participants who are involved in a specific patient's care. When a practitioner is a member of a CareTeam (directly, via their PractitionerRole, or via their Organization), the CareTeam validator grants them access to that patient's data.
CareTeam does not require changing the practitioner's organizational memberships. Dr. Lee at Clinic B can be added to Jane Doe's CareTeam (a patient at Clinic A) without getting a PractitionerRole at Clinic A. This means Dr. Lee sees only Jane Doe's data from Clinic A, not all of Clinic A's patients.
Because rules are additive (see above), Dr. Lee still retains full access to all Clinic B patients via LegitimateInterest, and gains access to Jane Doe via CareTeam.
Complete authorization configuration
Here is a comprehensive application.yaml authorization block. It covers all resource types and operations needed for a multi-tenant healthcare platform, grouped by role tier.
fire-arrow:
authorization:
default-validator: Forbidden
validation-rules:
# =========================================================
# DOCTORS - Full clinical access within their organization
# =========================================================
# Patient records
- client-role: Practitioner
resource: Patient
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Patient
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Patient
operation: update
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Patient
operation: create
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
# Clinical data (read + write)
- client-role: Practitioner
resource: Observation
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Observation
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Observation
operation: create
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Condition
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Condition
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Condition
operation: create
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: MedicationRequest
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: MedicationRequest
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: MedicationRequest
operation: create
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: CarePlan
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: CarePlan
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: CarePlan
operation: create
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: CarePlan
operation: update
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Encounter
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Encounter
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Encounter
operation: create
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Task
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Task
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Task
operation: update
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: DocumentReference
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: DocumentReference
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: DocumentReference
operation: create
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
# Doctors can upload files and subscribe to notifications
- client-role: Practitioner
resource: Binary
operation: binary-upload
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
- client-role: Practitioner
resource: Subscription
operation: subscribe
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: doctor
# =========================================================
# NURSES - Read-only clinical access
# =========================================================
- client-role: Practitioner
resource: Patient
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: Patient
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: Observation
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: Observation
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: Condition
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: Condition
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: MedicationRequest
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: MedicationRequest
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: CarePlan
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: CarePlan
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: Task
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
- client-role: Practitioner
resource: Task
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: nurse
# =========================================================
# IT ADMINISTRATORS - Organizational resources only
# =========================================================
- client-role: Practitioner
resource: Practitioner
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: Practitioner
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: PractitionerRole
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: PractitionerRole
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: Organization
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: Device
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: Device
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: Device
operation: create
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: Location
operation: read
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
- client-role: Practitioner
resource: Location
operation: search
validator: LegitimateInterest
practitioner-role-system: http://terminology.hl7.org/CodeSystem/practitioner-role
practitioner-role-code: ict
# Note: IT administrators have NO rules for Patient, Observation,
# Condition, MedicationRequest, or other clinical resources.
# Any attempt to access them falls through to "Forbidden".
# =========================================================
# CROSS-CLINIC SPECIALISTS - CareTeam-based access
# =========================================================
- client-role: Practitioner
resource: Patient
operation: read
validator: CareTeam
- client-role: Practitioner
resource: Patient
operation: search
validator: CareTeam
- client-role: Practitioner
resource: Observation
operation: read
validator: CareTeam
- client-role: Practitioner
resource: Observation
operation: search
validator: CareTeam
- client-role: Practitioner
resource: Condition
operation: read
validator: CareTeam
- client-role: Practitioner
resource: Condition
operation: search
validator: CareTeam
- client-role: Practitioner
resource: MedicationRequest
operation: read
validator: CareTeam
- client-role: Practitioner
resource: MedicationRequest
operation: search
validator: CareTeam
- client-role: Practitioner
resource: CarePlan
operation: read
validator: CareTeam
# =========================================================
# PATIENTS - Own data + organizational resources
# =========================================================
- client-role: Patient
resource: Patient
operation: read
validator: LegitimateInterest
- client-role: Patient
resource: Patient
operation: me
validator: PatientCompartment
- client-role: Patient
resource: Observation
operation: read
validator: PatientCompartment
- client-role: Patient
resource: Observation
operation: search
validator: PatientCompartment
- client-role: Patient
resource: Condition
operation: read
validator: PatientCompartment
- client-role: Patient
resource: Condition
operation: search
validator: PatientCompartment
- client-role: Patient
resource: MedicationRequest
operation: read
validator: PatientCompartment
- client-role: Patient
resource: MedicationRequest
operation: search
validator: PatientCompartment
- client-role: Patient
resource: CarePlan
operation: read
validator: PatientCompartment
- client-role: Patient
resource: CarePlan
operation: search
validator: PatientCompartment
- client-role: Patient
resource: Task
operation: read
validator: PatientCompartment
- client-role: Patient
resource: Task
operation: search
validator: PatientCompartment
- client-role: Patient
resource: Task
operation: update
validator: PatientCompartment
# Patients can see practitioners in their organization
- client-role: Patient
resource: Practitioner
operation: read
validator: LegitimateInterest
- client-role: Patient
resource: Practitioner
operation: search
validator: LegitimateInterest
- client-role: Patient
resource: Organization
operation: read
validator: LegitimateInterest
# =========================================================
# ALL PRACTITIONERS - Identity verification
# =========================================================
- client-role: Practitioner
resource: Practitioner
operation: me
validator: Allowed
# Role inheritance configuration
validators:
legitimate-interest:
role-inheritance-levels: 2
care-team:
max-recursion-depth: 2
The CareTeam rules above do not have practitioner-role-code restrictions. This is intentional: CareTeam-based access should apply to any practitioner who is a member of the CareTeam, regardless of their organizational role. If you want to restrict CareTeam access to certain roles, you can add care-team-role to filter by the participant's role within the CareTeam itself. See CareTeam Validator for details.
Step-by-step instructions
Step 1: Design and create the organization hierarchy
Create the Organization resources that represent your platform structure.
Via the Web UI: Open Organizations in the sidebar and click + Create Organization. Create the root organization first, then the clinics with partOf pointing to the root.
Via the API:
# Create the platform root organization
curl -X POST http://localhost:8080/fhir/Organization \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Organization",
"id": "healthtech-platform",
"name": "HealthTech Platform",
"active": true
}'
# Create Clinic A as a child of the platform
curl -X POST http://localhost:8080/fhir/Organization \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Organization",
"id": "clinic-a",
"name": "Downtown Family Clinic",
"active": true,
"partOf": {
"reference": "Organization/healthtech-platform"
}
}'
# Create Clinic B as a child of the platform
curl -X POST http://localhost:8080/fhir/Organization \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Organization",
"id": "clinic-b",
"name": "Westside Specialty Center",
"active": true,
"partOf": {
"reference": "Organization/healthtech-platform"
}
}'
Step 2: Create practitioners with role codes
Create Practitioner resources and PractitionerRole resources that link them to organizations with specific role codes.
Via the Web UI: Open Practitioners and create each practitioner, then add PractitionerRoles with the appropriate organization and code.
Via the API:
# Create a doctor at Clinic A
curl -X POST http://localhost:8080/fhir/Practitioner \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Practitioner",
"id": "dr-smith",
"name": [{ "family": "Smith", "given": ["Sarah"], "prefix": ["Dr."] }],
"active": true
}'
curl -X POST http://localhost:8080/fhir/PractitionerRole \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "PractitionerRole",
"practitioner": { "reference": "Practitioner/dr-smith" },
"organization": { "reference": "Organization/clinic-a" },
"code": [{
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/practitioner-role",
"code": "doctor",
"display": "Doctor"
}]
}],
"active": true
}'
# Create a nurse at Clinic A
curl -X POST http://localhost:8080/fhir/Practitioner \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Practitioner",
"id": "nurse-jones",
"name": [{ "family": "Jones", "given": ["Emily"] }],
"active": true
}'
curl -X POST http://localhost:8080/fhir/PractitionerRole \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "PractitionerRole",
"practitioner": { "reference": "Practitioner/nurse-jones" },
"organization": { "reference": "Organization/clinic-a" },
"code": [{
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/practitioner-role",
"code": "nurse",
"display": "Nurse"
}]
}],
"active": true
}'
# Create a support team member at the platform root
curl -X POST http://localhost:8080/fhir/Practitioner \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Practitioner",
"id": "support-admin",
"name": [{ "family": "Support", "given": ["Platform"] }],
"active": true
}'
curl -X POST http://localhost:8080/fhir/PractitionerRole \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "PractitionerRole",
"practitioner": { "reference": "Practitioner/support-admin" },
"organization": { "reference": "Organization/healthtech-platform" },
"code": [{
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/practitioner-role",
"code": "doctor",
"display": "Doctor"
}]
}],
"active": true
}'
Step 3: Register patients under clinics
Create patients and assign them to their clinic via managingOrganization.
Via the Web UI: Open Patients, click + Create Patient, and set the Managing Organization field.
Via the API:
curl -X POST http://localhost:8080/fhir/Patient \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Patient",
"id": "jane-doe",
"name": [{ "family": "Doe", "given": ["Jane"] }],
"gender": "female",
"birthDate": "1985-03-15",
"managingOrganization": {
"reference": "Organization/clinic-a"
}
}'
Step 4: Configure the authorization rules
Copy the complete authorization configuration from above into your application.yaml. Restart the server.
Walk through the configuration with your team to ensure everyone understands the access model:
- Doctors at Clinic A can read and write Patient, Observation, Condition, MedicationRequest, CarePlan, Encounter, Task, and DocumentReference - but only for patients whose
managingOrganizationis Clinic A. - Nurses at Clinic A can read the same resources but cannot create or modify them.
- IT administrators can manage Practitioners, PractitionerRoles, Organizations, Devices, and Locations - but have zero access to any clinical data. There are no Patient, Observation, or other clinical rules for the
ictcode, so those requests fall through to theForbiddendefault. - Patients can read their own clinical data (via PatientCompartment) and browse their organization's practitioners and resources (via LegitimateInterest).
Step 5: Enable role inheritance
The authorization configuration already includes role-inheritance-levels: 2. This means the support team member (with a PractitionerRole at the root "HealthTech Platform" organization) can see data in all clinics - Platform (direct) + Clinic A (1 level) + Clinic B (1 level).
To verify:
# Support team member searches for patients across all clinics
curl "http://localhost:8080/fhir/Patient" \
-H "Authorization: Bearer <support-team-token>"
# Should return patients from both Clinic A and Clinic B
Each level of role inheritance adds database queries to resolve the organization hierarchy. For a platform with a few clinics, role-inheritance-levels: 2 is fine. For deeply nested hierarchies (5+ levels), test query performance under realistic data volumes before deploying to production. See LegitimateInterest > Performance for details.
Step 6: Set up cross-clinic CareTeam access
When Dr. Lee at Clinic B needs to consult on Jane Doe (a patient at Clinic A), create a CareTeam:
curl -X POST http://localhost:8080/fhir/CareTeam \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "CareTeam",
"status": "active",
"subject": {
"reference": "Patient/jane-doe"
},
"participant": [
{
"role": [{
"coding": [{
"system": "http://snomed.info/sct",
"code": "309343006",
"display": "Physician"
}]
}],
"member": {
"reference": "Practitioner/dr-lee"
}
}
]
}'
Now Dr. Lee can access Jane Doe's data through the CareTeam validator rules, while still seeing all Clinic B patients through the LegitimateInterest rules. This is the additive behavior: both scopes are combined.
Step 7: Test and verify
Verify identity resolution with $me:
curl http://localhost:8080/fhir/\$me \
-H "Authorization: Bearer <dr-smith-token>"
# Should return: Practitioner/dr-smith
Verify data isolation:
# Dr. Smith (Clinic A) searches for patients
curl "http://localhost:8080/fhir/Patient" \
-H "Authorization: Bearer <dr-smith-token>"
# Should return ONLY Clinic A patients
# Dr. Lee (Clinic B) searches for patients
curl "http://localhost:8080/fhir/Patient" \
-H "Authorization: Bearer <dr-lee-token>"
# Should return Clinic B patients PLUS Jane Doe (via CareTeam)
# IT Admin searches for patients
curl "http://localhost:8080/fhir/Patient" \
-H "Authorization: Bearer <it-admin-token>"
# Should return 403 Forbidden (no patient access rules for ict)
Use debug mode for troubleshooting:
# If a request is unexpectedly denied, use debug mode
curl -H "Authorization: Bearer <token>" \
-H "X-Fire-Arrow-Debug: true" \
http://localhost:8080/fhir/Patient/jane-doe
The debug output shows every rule that was evaluated and why it matched or didn't. See Authorization Debug Mode for how to interpret the output.
Do not enable debug mode in production. The debug output exposes your full authorization configuration. Use it only in development and staging environments.
Further reading
- Authorization Concepts - how the rule-based authorization system works.
- LegitimateInterest Validator - organization-based access control, role filtering, role inheritance.
- CareTeam Validator - cross-organizational access via CareTeam membership.
- Identity Filters - FHIRPath-based conditions on the client's identity resource.
- Property Filters - redacting sensitive fields from responses (useful for research scenarios).
- Authorization Debug Mode - troubleshooting denied requests.
- Web UI: Organizations - managing organizations through the web interface.
- Web UI: Practitioners - managing practitioners and roles.
- Web UI: Patients - managing patient records.
- Web UI: Roles - managing UI feature visibility per role.