Skip to content

Security Strategy

Intent

Provide pluggable authentication and authorization strategies for different FHIR access contexts (EHR launch, standalone apps, backend services) using the Strategy design pattern.

Forces

Structure

The Security Strategy pattern implements different authentication and authorization approaches as pluggable strategies, allowing the same system to handle multiple SMART on FHIR flows.

Security Strategy Class Diagram

Key Components

AuthStrategy

Interface defining the contract for authentication and authorization strategies

EhrLaunchStrategy

Handles SMART EHR launch flow with contextual patient/encounter information

StandaloneStrategy

Manages standalone app launch where users authenticate directly

BackendServicesStrategy

Implements SMART Backend Services with client credentials and JWT assertion

ScopeEvaluator

Evaluates FHIR scopes against specific operations and resource types

SecurityGateway

Context object that selects and delegates to appropriate strategy

Behavior

Strategy Selection Logic

The SecurityGateway determines which strategy to use based on request characteristics:

Security Strategy Flow

Backend Services Flow

For system-to-system integration using client credentials:

Backend Services Flow
# Example JWT assertion for backend services
{
  "iss": "client-id-12345",
  "sub": "client-id-12345", 
  "aud": "https://fhir.example.org/token",
  "jti": "unique-request-id",
  "exp": 1609459200,
  "iat": 1609458900,
  "nbf": 1609458900
}

# Requested scopes
scope: "system/Patient.read system/Observation.read"

EHR Launch Context

For apps launched from an EHR with clinical context:

EHR Launch Context
# Access token claims
{
  "iss": "https://auth.example.org",
  "sub": "user-123",
  "aud": "client-app-456",
  "scope": "patient/Patient.read patient/Observation.read launch",
  "launch": "launch-context-789",
  "patient": "Patient/123",
  "encounter": "Encounter/456"
}

Implementation Considerations

Strategy Configuration

Configures multiple authentication strategies (EHR launch, standalone, backend services) with pluggable validation and scope handling.

Strategy Configuration
# Example strategy configuration
class SecurityConfig:
    def __init__(self):
        self.strategies = {
            'ehr-launch': EhrLaunchStrategy(
                issuer='https://auth.hospital.org',
                jwks_url='https://auth.hospital.org/.well-known/jwks.json'
            ),
            'backend': BackendServicesStrategy(
                allowed_clients=['system-a', 'system-b'],
                token_endpoint='https://auth.hospital.org/token'
            ),
            'standalone': StandaloneStrategy(
                issuer='https://auth.hospital.org',
                introspection_endpoint='https://auth.hospital.org/introspect'
            )
        }

Scope Evaluation

Parses and evaluates FHIR scopes (patient/*.read, user/Observation.write) against requested operations and resource types.

Scope Evaluation
class ScopeEvaluator:
    def permit(self, operation: str, resource_type: str, scopes: List[str]) -> bool:
        # Check for wildcard permissions
        if f"user/*.{operation}" in scopes:
            return True
        if f"patient/*.{operation}" in scopes:
            return True
        if f"system/*.{operation}" in scopes:
            return True

        # Check resource-specific permissions
        if f"user/{resource_type}.{operation}" in scopes:
            return True
        if f"patient/{resource_type}.{operation}" in scopes:
            return True
        if f"system/{resource_type}.{operation}" in scopes:
            return True

        return False
FHIR scopes follow specific patterns that need consistent evaluation:

Token Validation

Validates JWT access tokens including signature verification, expiration checks, and issuer validation against configured authorization servers.

Token Validation
class BackendServicesStrategy(AuthStrategy):
    def authorize(self, request: FhirRequest) -> AuthzResult:
        token = self.extract_token(request)

        # Validate JWT signature and claims
        claims = self.validate_jwt(token)

        # Verify client registration
        if not self.is_registered_client(claims['iss']):
            raise UnauthorizedError("Unknown client")

        # Check token freshness
        if claims['exp'] < time.time():
            raise UnauthorizedError("Token expired")

        return AuthzResult(
            authorized=True,
            scopes=claims.get('scope', '').split(),
            context={'client_id': claims['iss']}
        )
Each strategy implements appropriate token validation:

Context Propagation

Propagates security context through request chains for multi-hop scenarios, preserving user identity and authorization decisions. Security context often needs to be propagated through request chains:

Context Propagation
class SecurityContext:
    def __init__(self, user_id: str = None, patient_id: str = None, 
                 scopes: List[str] = None, client_id: str = None):
        self.user_id = user_id
        self.patient_id = patient_id
        self.scopes = scopes or []
        self.client_id = client_id

    def has_scope(self, scope: str) -> bool:
        return scope in self.scopes

    def can_access_patient(self, patient_id: str) -> bool:
        # Backend services can access any patient
        if any(s.startswith('system/') for s in self.scopes):
            return True

        # Patient-scoped access limited to authorized patient
        return self.patient_id == patient_id

  • Broker: Broker delegates authentication and authorization to Security Strategy
  • Privacy Enforcement: Privacy Enforcement relies on Security Strategy to establish user/system identity for consent decisions
  • Audit & Provenance Chain: Security context from Security Strategy is captured in audit events
  • Population Export Pipeline: Population Export uses Backend Services strategy for bulk data access

Benefits

  • Flexibility: Support multiple authentication flows in same system
  • Extensibility: Easy to add new authentication strategies
  • Separation of Concerns: Authentication logic isolated from business logic
  • Standards Compliance: Implements SMART on FHIR specifications correctly
  • Testing: Easy to mock and test different authentication scenarios

Trade-offs

  • Complexity: More complex than single authentication approach
  • Performance: Strategy selection and validation overhead
  • Configuration: Multiple strategies require more configuration management
  • Token Handling: Different token formats and validation requirements

References


Strategy Selection

Use request characteristics (headers, token format, claims) to automatically select the appropriate strategy rather than requiring explicit configuration per request.

Security Considerations

Always validate tokens cryptographically and check expiration times. Implement proper rate limiting and audit logging for all authentication attempts.