Understanding Medplum's User Management: A Comprehensive Guide to FHIR-Native Access Control

Healthcare applications require sophisticated user management systems that can handle complex organizational structures, diverse user roles, and strict access controls. Medplum, as a FHIR-native healt

 · 9 min read

Understanding Medplum's User Management: A Comprehensive Guide to FHIR-Native Access Control

Healthcare applications demand robust user management systems capable of handling diverse roles and strict data privacy requirements. Medplum provides a powerful, FHIR-native solution that integrates identity, authorization, and access control directly into the healthcare data model. This comprehensive guide explores Medplum's user management architecture, demonstrating how to build secure and scalable healthcare applications.

Table of Contents

  1. Introduction to Medplum's User Management
  2. Core Concepts
  3. The Four Pillars of Medplum User Management
  4. Access Control with AccessPolicy
  5. Real-World Use Cases
  6. Implementation Examples
  7. Best Practices
  8. Conclusion

Introduction to Medplum's User Management

Traditional healthcare platforms often struggle with user management complexity, requiring custom identity systems that don't align with healthcare data standards. Medplum takes a different approach by building user management directly on top of FHIR (Fast Healthcare Interoperability Resources) standards, creating a unified system where user identities, roles, and permissions are represented as FHIR resources.

This FHIR-native approach provides several key advantages:

  • Standards Compliance: All user management follows established healthcare interoperability standards
  • Unified Data Model: User identities and healthcare data use the same resource model
  • Granular Access Control: Fine-grained permissions down to individual fields and resource instances
  • Audit Trail: Complete audit logging using FHIR AuditEvent resources
  • Interoperability: User management data can be shared across FHIR-compliant systems

Core Concepts

Medplum's user management revolves around three key resources that work together to define who a user is and what they can do.

FHIR-First Architecture

Medplum treats user management as a first-class FHIR implementation concern. Rather than bolting on authentication and authorization as separate systems, user identities, roles, and permissions are represented as FHIR resources that can be queried, updated, and managed using standard FHIR operations.

Multi-Tenancy Through Projects

Medplum uses the concept of "Projects" to provide multi-tenancy. Each project represents a separate healthcare organization, department, or logical boundary with its own set of users, data, and access policies. This enables a single Medplum instance to serve multiple organizations while maintaining strict data isolation.

Profile-Based Role Management

Instead of abstract role names, Medplum uses FHIR resource types to represent user roles. A user's "profile" is literally a FHIR resource (Patient, Practitioner, RelatedPerson, etc.) that defines their role and attributes within the healthcare context. This is a crucial concept: roles are not abstract labels but tangible data resources.

The Four Pillars of Medplum User Management

Medplum's user management system is built on four interconnected FHIR resources that work together to provide comprehensive identity and access management:

1. User Resource: The Foundation of Identity

A User is the global identity for an individual, representing their login credentials (e.g., email and password). A single User account can be associated with multiple projects, allowing a person to log in once and access different healthcare workspaces.

The User resource contains:

{
  "resourceType": "User",
  "id": "user-123",
  "email": "dr.smith@hospital.com",
  "firstName": "John",
  "lastName": "Smith",
  "active": true,
  "emailVerified": true
}

Key Characteristics: - Global identity across all projects - Contains authentication credentials and basic profile information - Manages email verification, password policies, and MFA settings - Can be associated with multiple projects through ProjectMembership resources

2. Project Resource

The Project resource defines organizational boundaries and configuration:

{
  "resourceType": "Project",
  "id": "hospital-main",
  "name": "General Hospital - Main Campus",
  "description": "Primary hospital system for General Hospital",
  "owner": {
    "reference": "User/admin-user"
  },
  "features": ["bots", "email", "graphql"]
}

Key Characteristics: - Provides multi-tenant data isolation - Defines available features and capabilities - Contains project-wide configuration settings - Manages billing and resource limits

3. Profile Resources: Defining Roles in a Healthcare Context

In Medplum, a Profile is a specific FHIR resource that represents a user's role within a particular project. This is a crucial concept: roles are not abstract labels but tangible data resources. Profile resources represent the user's role and identity within the healthcare context. Medplum supports several profile types:

For Human Users:

Patient Profile:

{
  "resourceType": "Patient",
  "id": "patient-456",
  "identifier": [
    {
      "system": "http://hospital.com/patient-id",
      "value": "MRN-789"
    }
  ],
  "name": [
    {
      "family": "Johnson",
      "given": ["Mary", "Elizabeth"]
    }
  ],
  "telecom": [
    {
      "system": "email",
      "value": "mary.johnson@email.com"
    }
  ]
}

Practitioner Profile:

{
  "resourceType": "Practitioner",
  "id": "practitioner-789",
  "identifier": [
    {
      "system": "http://hl7.org/fhir/sid/us-npi",
      "value": "1234567890"
    }
  ],
  "name": [
    {
      "family": "Smith",
      "given": ["John"],
      "prefix": ["Dr."]
    }
  ],
  "qualification": [
    {
      "code": {
        "coding": [
          {
            "system": "http://terminology.hl7.org/CodeSystem/v2-0360",
            "code": "MD",
            "display": "Doctor of Medicine"
          }
        ]
      }
    }
  ]
}

RelatedPerson Profile:

{
  "resourceType": "RelatedPerson",
  "id": "related-person-101",
  "patient": {
    "reference": "Patient/patient-456"
  },
  "relationship": [
    {
      "coding": [
        {
          "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode",
          "code": "GUARD",
          "display": "Guardian"
        }
      ]
    }
  ],
  "name": [
    {
      "family": "Johnson",
      "given": ["Robert"]
    }
  ]
}

#### For Programmatic Access:

ClientApplication Profile:

{
  "resourceType": "ClientApplication",
  "id": "mobile-app-client",
  "name": "Hospital Mobile App",
  "description": "Patient-facing mobile application",
  "redirectUri": "https://app.hospital.com/callback"
}

Bot Profile:

{
  "resourceType": "Bot",
  "id": "integration-bot",
  "name": "ERP Integration Bot",
  "description": "Automated sync between ERP and FHIR systems",
  "code": "// Bot implementation code here"
}

4. ProjectMembership Resource: The Glue That Connects Everything

A ProjectMembership resource is the link that connects a User to a Profile within a specific Project. It also assigns an AccessPolicy, which dictates the user's permissions.

In short, a ProjectMembership answers the question: "Which User can act as which Profile with what AccessPolicy in this Project?"

The ProjectMembership resource is the crucial link that connects Users, Projects, Profiles, and Access Policies:

{
  "resourceType": "ProjectMembership",
  "id": "membership-123",
  "project": {
    "reference": "Project/hospital-main"
  },
  "user": {
    "reference": "User/user-123"
  },
  "profile": {
    "reference": "Practitioner/practitioner-789"
  },
  "accessPolicy": {
    "reference": "AccessPolicy/practitioner-policy"
  },
  "admin": false
}

Key Characteristics: - Creates the binding between User identity and Project context - Links to the user's Profile resource within the project - References the AccessPolicy that defines permissions - Can mark users as project administrators - Supports multiple memberships per user for different roles

## Access Control with AccessPolicy

Medplum's AccessPolicy resource provides sophisticated, FHIR-native access control that goes far beyond simple role-based permissions. It enables fine-grained control over what users can do, what data they can access, and how they can interact with resources.

### AccessPolicy Structure

{
  "resourceType": "AccessPolicy",
  "id": "practitioner-policy",
  "name": "General Practitioner Access Policy",
  "resource": [
    {
      "resourceType": "Patient",
      "criteria": "Patient?general-practitioner=Practitioner/{%user.profile.id}",
      "interaction": ["create", "read", "update", "search"],
      "readonly": ["id", "meta"]
    },
    {
      "resourceType": "Observation",
      "compartment": {
        "reference": "Patient/{%patient.id}"
      },
      "interaction": ["create", "read", "update", "search", "history"]
    }
  ]
}

### Access Control Capabilities

#### 1. Resource-Level Permissions

Control access to entire FHIR resource types:

{
  "resourceType": "Medication",
  "interaction": ["read", "search"]
}

#### 2. Interaction-Specific Controls

Define exactly what operations users can perform:

  • create: Create new resources
  • read: View specific resources by ID
  • update: Modify existing resources
  • delete: Soft-delete resources
  • search: Search for resources
  • history: View resource version history
  • vread: View specific resource versions

    3. Criteria-Based Access

    Use FHIR search parameters to limit access to specific resource instances:

    {
      "resourceType": "Patient",
      "criteria": "Patient?organization=Organization/my-clinic"
    }
    

    4. Compartment-Based Access

    Grant access to all resources within a FHIR compartment:

    {
      "resourceType": "*",
      "compartment": {
        "reference": "Patient/{%patient.id}"
      }
    }
    

    5. Field-Level Controls

    Control access to specific fields within resources:

    {
      "resourceType": "Patient",
      "readonly": ["id", "meta", "identifier"],
      "hidden": ["telecom", "address"]
    }
    

    6. Write Constraints

    Use FHIRPath expressions to enforce business rules:

    {
      "resourceType": "Observation",
      "writeConstraint": [
        {
          "expression": "status = 'preliminary' or status = 'final'",
          "description": "Observation status must be preliminary or final"
        }
      ]
    }
    

    Real-World Use Cases

Use Case 1: Multi-Role Healthcare Provider - The Power of Multiple Roles

A single User can have multiple ProjectMembership resources within the same project. This is the standard way to handle cases where a person needs to operate in different roles.

Real-World Example: The Doctor Who is Also a Patient

Dr. Sarah Chen works as both a physician and occasionally receives care as a patient at the same hospital system. This person would have a single User account but two separate ProjectMemberships:

Implementation: - Single User resource for Dr. Chen - Two ProjectMembership resources: 1. Practitioner Membership: - User: The doctor's login account - Profile: A Practitioner resource - AccessPolicy: A policy granting broad access to clinical data 2. Patient Membership: - User: The same doctor's login account - Profile: A Patient resource - AccessPolicy: A restrictive policy that only allows them to view their own records

// Practitioner Membership
{
  "resourceType": "ProjectMembership",
  "user": {"reference": "User/dr-chen"},
  "profile": {"reference": "Practitioner/chen-md"},
  "accessPolicy": {"reference": "AccessPolicy/attending-physician"}
}

// Patient Membership  
{
  "resourceType": "ProjectMembership",
  "user": {"reference": "User/dr-chen"},
  "profile": {"reference": "Patient/chen-patient"},
  "accessPolicy": {"reference": "AccessPolicy/patient-portal"}
}

When the user logs in, the application can allow them to switch between these roles, and the corresponding AccessPolicy is applied for that session.

### Use Case 2: Family Member Access

Robert Johnson needs access to manage healthcare decisions for his elderly mother, Mary Johnson.

Implementation: - User resource for Robert - RelatedPerson profile linking him to his mother - AccessPolicy granting access to his mother's compartment

{
  "resourceType": "ProjectMembership",
  "user": {"reference": "User/robert-johnson"},
  "profile": {"reference": "RelatedPerson/robert-mary-guardian"},
  "accessPolicy": {"reference": "AccessPolicy/family-caregiver"}
}

### Use Case 3: Research Access

A clinical researcher needs access to de-identified patient data for a specific study.

Implementation: - Specialized AccessPolicy with hidden PII fields - Criteria-based access to study participants only

{
  "resourceType": "AccessPolicy",
  "resource": [
    {
      "resourceType": "Patient",
      "criteria": "Patient?_tag=study-cohort-2024",
      "hidden": ["name", "telecom", "address", "identifier"],
      "interaction": ["read", "search"]
    }
  ]
}

## Implementation Examples

### Creating a New User with Multiple Roles

// 1. Create the User resource
const user = await medplum.createResource({
  resourceType: 'User',
  email: 'nurse.williams@hospital.com',
  firstName: 'Jennifer',
  lastName: 'Williams'
});

// 2. Create the Practitioner profile
const practitioner = await medplum.createResource({
  resourceType: 'Practitioner',
  name: [{
    family: 'Williams',
    given: ['Jennifer'],
    prefix: ['RN']
  }],
  qualification: [{
    code: {
      coding: [{
        system: 'http://terminology.hl7.org/CodeSystem/v2-0360',
        code: 'RN',
        display: 'Registered Nurse'
      }]
    }
  }]
});

// 3. Create the ProjectMembership
const membership = await medplum.createResource({
  resourceType: 'ProjectMembership',
  user: { reference: `User/${user.id}` },
  profile: { reference: `Practitioner/${practitioner.id}` },
  accessPolicy: { reference: 'AccessPolicy/registered-nurse-policy' }
});

### Dynamic Access Policy Creation

// Create an access policy for a specific department
const createDepartmentPolicy = async (departmentId: string) => {
  return await medplum.createResource({
    resourceType: 'AccessPolicy',
    name: `Department ${departmentId} Access Policy`,
    resource: [
      {
        resourceType: 'Patient',
        criteria: `Patient?general-practitioner.organization=Organization/${departmentId}`,
        interaction: ['create', 'read', 'update', 'search']
      },
      {
        resourceType: 'Encounter',
        criteria: `Encounter?service-provider=Organization/${departmentId}`,
        interaction: ['create', 'read', 'update', 'search']
      },
      {
        resourceType: 'Observation',
        compartment: {
          reference: 'Patient/{%patient.id}'
        },
        interaction: ['create', 'read', 'update', 'search']
      }
    ]
  });
};

### Checking User Permissions

// Check if current user can access a specific patient
const canAccessPatient = async (patientId: string) => {
  try {
    const patient = await medplum.readResource('Patient', patientId);
    return true; // If no error, user has access
  } catch (error) {
    if (error.status === 403) {
      return false; // Access denied
    }
    throw error; // Other error
  }
};

// Get all patients accessible to current user
const getAccessiblePatients = async () => {
  return await medplum.searchResources('Patient');
  // This automatically applies access policy filters
};

## Best Practices

### 1. Design Principle-Based Access Policies

Create access policies based on healthcare principles rather than technical convenience:

{
  "name": "Principle of Least Privilege - Nurse",
  "description": "Nurses can access patient data for their assigned patients only",
  "resource": [
    {
      "resourceType": "Patient",
      "criteria": "Patient?care-team.participant=Practitioner/{%user.profile.id}",
      "interaction": ["read", "update", "search"]
    }
  ]
}

### 2. Use Compartment-Based Access for Patient Data

Leverage FHIR compartments to ensure related resources are accessible together:

{
  "resourceType": "*",
  "compartment": {
    "reference": "Patient/{%patient.id}"
  },
  "interaction": ["read", "search"]
}

### 3. Implement Proper Audit Logging

Ensure all access is logged using FHIR AuditEvent resources:

const logAccess = async (resourceType: string, resourceId: string, action: string) => {
  await medplum.createResource({
    resourceType: 'AuditEvent',
    type: {
      system: 'http://terminology.hl7.org/CodeSystem/audit-event-type',
      code: 'rest'
    },
    action: action,
    recorded: new Date().toISOString(),
    agent: [{
      who: {
        reference: `User/${getCurrentUser().id}`
      }
    }],
    entity: [{
      what: {
        reference: `${resourceType}/${resourceId}`
      }
    }]
  });
};

### 4. Regular Access Policy Reviews

Implement automated checks for access policy effectiveness:

const reviewAccessPolicies = async () => {
  const policies = await medplum.searchResources('AccessPolicy');

  for (const policy of policies) {
    // Check for overly broad permissions
    const broadAccess = policy.resource?.some(r => 
      r.resourceType === '*' && !r.criteria && !r.compartment
    );

    if (broadAccess) {
      console.warn(`Policy ${policy.id} has overly broad access`);
    }
  }
};

### 5. Handle Multiple Memberships Gracefully

When users have multiple roles, provide clear context switching:

const switchUserContext = async (membershipId: string) => {
  const membership = await medplum.readResource('ProjectMembership', membershipId);

  // Update session context
  await medplum.setActiveLogin({
    membership: membership,
    profile: membership.profile
  });

  // Refresh UI to reflect new permissions
  window.location.reload();
};

## Conclusion

Medplum's FHIR-native approach to user management represents a significant advancement in healthcare platform architecture. By treating user identities, roles, and permissions as first-class FHIR resources, Medplum provides:

  • Standards Compliance: Full alignment with healthcare interoperability standards
  • Granular Control: Fine-grained permissions down to individual fields and resource instances
  • Scalability: Multi-tenant architecture supporting complex organizational structures
  • Flexibility: Support for multiple user roles and dynamic permission models
  • Auditability: Complete audit trails using standard FHIR resources

    This approach enables healthcare organizations to build sophisticated applications that meet strict regulatory requirements while maintaining the flexibility needed for complex healthcare workflows. Whether you're building patient portals, clinical decision support systems, or administrative dashboards, Medplum's user management system provides the foundation for secure, compliant, and scalable healthcare applications.

    The key to success with Medplum's user management is understanding that it's not just about authentication and authorization—it's about creating a unified healthcare data model where user identities are seamlessly integrated with clinical data, enabling new possibilities for personalized, secure, and interoperable healthcare applications.


This blog post provides a comprehensive overview of Medplum's user management capabilities. For the most up-to-date documentation and implementation details, please refer to the official Medplum documentation.


A
Atul-Kuruvilla

Github: pythonpen

No comments yet.

Add a comment
Ctrl+Enter to add comment