Skip to main content

Auditing & Provenance

Comprehensive audit logging is essential for healthcare compliance (HIPAA, GDPR) and security monitoring. WSO2 Open Healthcare records FHIR interactions as AuditEvent resources through a dedicated audit service that integrates with FHIR facade APIs built using the Ballerina FHIR R4 service type.

Overview

When audit logging is enabled on a FHIR API, each completed request produces an audit record that captures:

  • Who accessed or modified data (agent identification)
  • What resources were accessed (entity tracking)
  • When the action occurred (timestamp)
  • Where the request originated (source observer)
  • How the access was performed (REST interaction and action code)
  • Outcome of the request (success or failure)

Audit Service

The WSO2 Healthcare Accelerator includes a pre-built Audit Service that accepts flattened audit payloads and persists them as FHIR R4 AuditEvent resources. The service is available in the open-healthcare-choreo-accelerators repository under miscellaneous/audit-service.

Features

  • Accepts audit events via a lightweight REST API (POST /audits)
  • Converts incoming payloads to FHIR R4 AuditEvent resources with terminology-backed codings
  • Appends audit events as newline-delimited JSON to a configurable log file
  • Buffers failed writes in an in-memory cache and retries on a scheduled interval

API Endpoint

OperationEndpointDescription
Post audit eventPOST /auditsAccept an audit payload and persist it as an AuditEvent

The service listens on port 9093 by default.

Request Payload

The audit service accepts an InternalAuditEvent payload—a flattened representation of key AuditEvent fields:

FieldMaps toDescription
typeCodeAuditEvent.typeEvent type (default: rest)
subTypeCodeAuditEvent.subtypeREST interaction (for example, read, create, search-type)
actionCodeAuditEvent.actionAction performed (R, C, U, D, or E)
outcomeCodeAuditEvent.outcome0 for success, 8 for failure
recordedTimeAuditEvent.recordedUTC timestamp of the event
agentTypeAuditEvent.agent.typeSecurity role of the agent
agentNameAuditEvent.agent.who.displayDisplay name or ID of the agent
agentIsRequestorAuditEvent.agent.requestorWhether the agent initiated the request
sourceObserverNameAuditEvent.source.observer.displayName of the observing system
sourceObserverTypeAuditEvent.source.typeSource type code
entityTypeAuditEvent.entity.typeType of entity accessed
entityRoleAuditEvent.entity.roleRole of the entity in the event
entityWhatReferenceAuditEvent.entity.what.referenceRelative path of the resource (for example, Patient/example)

Configuration

Configure the audit service in Config.toml:

# Path to the audit log file (use a mounted volume in production)
auditLogPath = "/tmp/audit-logs/fhir-audit.log"

# Capacity of the in-memory cache for failed writes
cacheCapacity = 1000

# Default FHIR server name used as the source observer when not provided
fhirServerName = "wso2fhirserver.com"

# Default agent type when not provided in the payload
agentType = "humanuser"

Running the Service

Clone the accelerator repository and start the audit service:

git clone https://github.com/wso2/open-healthcare-choreo-accelerators.git
cd open-healthcare-choreo-accelerators/miscellaneous/audit-service
bal run

The service is available at http://localhost:9093.

Verifying the Service

Post a sample audit event:

curl -X POST http://localhost:9093/audits \
-H "Content-Type: application/json" \
-d '{
"typeCode": "rest",
"subTypeCode": "read",
"actionCode": "R",
"outcomeCode": "0",
"recordedTime": "2024-01-15T10:30:01Z",
"agentType": "humanuser",
"agentName": "dr-smith",
"agentIsRequestor": true,
"sourceObserverName": "",
"sourceObserverType": "3",
"entityType": "2",
"entityRole": "1",
"entityWhatReference": "Patient/123"
}'

On success, the service returns the generated FHIR AuditEvent resource. The event is also appended to the configured log file.

Enabling Audit on FHIR Facade APIs

FHIR facade APIs built with the Ballerina FHIR R4 service type can publish audit events to the audit service through the built-in audit handler. When a request completes, the FHIRResponseInterceptor sends the audit event asynchronously so that audit logging does not block the client response.

Audit Configuration

Add an auditConfig block to the ResourceAPIConfig record for each FHIR API that should emit audit events:

import ballerinax/health.fhir.r4;

public final r4:ResourceAPIConfig patientApiConfig = {
resourceType: "Patient",
profiles: [
"http://hl7.org/fhir/StructureDefinition/Patient"
],
defaultProfile: (),
searchParameters: [
// ... search parameter configuration
],
operations: [],
serverConfig: (),
authzConfig: (),
auditConfig: {
enabled: true,
auditServiceUrl: "http://localhost:9093"
}
};
FieldTypeDescription
enabledbooleanEnable or disable audit event publishing (default: false)
auditServiceUrlstringBase URL of the audit service

Pass the API config to the FHIR listener when defining the service:

import ballerinax/health.fhirr4;
import ballerinax/health.fhir.r4;

service / on new fhirr4:Listener(9090, patientApiConfig) {

isolated resource function get fhir/r4/Patient(r4:FHIRContext fhirContext) returns r4:Bundle {
// Implementation
}
}

Audit Handler Behavior

The audit handler (handleAuditEvent in ballerinax/health.fhir.r4) builds an InternalAuditEvent from the request context and posts it to POST /audits on the configured audit service. The handler populates the following fields automatically:

FieldSource
typeCodeFixed as rest
subTypeCodeFHIR REST interaction (for example, read, vread, create)
actionCodeDerived from interaction: R (read), C (create), U (update), D (delete), E (other)
outcomeCode0 on success, 8 when the request is in an error state
recordedTimeCurrent UTC time
agentNameAuthenticated user ID, or Unknown if not available
agentIsRequestortrue
sourceObserverType3 (Application Server)
entityType2 (System Object)
entityRole1 (Patient)
entityWhatReferenceRaw request path from the FHIR context

The HTTP client used to call the audit service is configured with retries (3 attempts, 5-second interval, exponential backoff). If publishing still fails after retries, the error and audit payload are logged by the FHIR facade.

AuditEvent Resource Structure

The audit service converts each InternalAuditEvent into a FHIR R4 AuditEvent resource. A typical event generated from a Patient read request looks like this:

{
"resourceType": "AuditEvent",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": {
"system": "http://terminology.hl7.org/CodeSystem/audit-event-type",
"code": "rest",
"display": "RESTful Operation"
},
"subtype": [{
"system": "http://hl7.org/fhir/restful-interaction",
"code": "read",
"display": "read"
}],
"action": "R",
"outcome": "0",
"recorded": "2024-01-15T10:30:01Z",
"agent": [{
"type": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type",
"code": "humanuser"
}]
},
"who": {"display": "dr-smith"},
"requestor": true
}],
"source": {
"observer": {"display": "wso2fhirserver.com"},
"type": [{
"system": "http://terminology.hl7.org/CodeSystem/security-source-type",
"code": "3"
}]
},
"entity": [{
"type": {
"system": "http://terminology.hl7.org/CodeSystem/audit-entity-type",
"code": "2"
},
"role": {
"system": "http://terminology.hl7.org/CodeSystem/object-role",
"code": "1"
},
"what": {"reference": "Patient/123"}
}]
}

Audit events are persisted as newline-delimited JSON in the configured log file. For FHIR-native storage and search, you can forward events to an AuditEvent repository or implement a custom persistence layer on top of the audit log.

Provenance

While AuditEvent resources record who did what and when, Provenance resources track the origin and lineage of clinical data—who created or transmitted a resource and what activity produced it.

Provenance is a separate FHIR resource type. Applications that need provenance tracking should create Provenance resources explicitly when data is created, updated, or transmitted. For example, US Core requires provenance information for transmitted data—see US Core Provenance Requirements.

{
"resourceType": "Provenance",
"target": [{"reference": "Observation/789"}],
"recorded": "2024-01-15T10:30:00Z",
"activity": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation",
"code": "CREATE"
}]
},
"agent": [
{
"type": {"coding": [{"code": "author"}]},
"who": {"reference": "Practitioner/dr-smith"}
},
{
"type": {"coding": [{"code": "transmitter"}]},
"who": {"reference": "Organization/hospital-a"}
}
]
}

Compliance

Audit logging supports regulatory requirements for access tracking and accountability:

RegulationRelevance
HIPAAAudit controls (§164.312(b)) require logging access to PHI
GDPRAccountability principle requires demonstrable access records