Skip to main content

Ghasi e-Prescribing Gateway Service — Security Model

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD

Authentication

  • Interactive users: Keycloak-issued JWT (Authorization: Bearer).
  • Service-to-service (EHR, Pharmacy backends): OAuth2 client credentials; JWT persona claim determines write rights.
  • B2B external integrators: client credentials + optional mTLS; scoped to gateway-only endpoints.
  • tenantId extracted exclusively from JWT tenant_id claim.

Persona-Based Write Authorization (RBAC)

Persona / RoleWrite MRWrite MDWrite TaskRegister SubscriptionAdmin operations
ehr-backend✅ (owner side)
pharmacy-backend✅ (requester side)
b2b-externalDepends on tenant IG scopeDepends on tenant IG scope
platform-admin✅ (replay, DLQ)
clinician (interactive)❌ (use orders-service)

Key constraint: A pharmacy-backend service account CANNOT write MedicationRequest. An ehr-backend service account CANNOT write MedicationDispense. This is enforced by the gateway even if the client presents a valid JWT (BR-RX-001, BR-RX-002).

Encryption

DataAt restIn transit
gepgw_* Postgres tablesAES-256 via cloud KMS (tenant-level key)TLS 1.3
FHIR resource JSONB columnsEncrypted at field level for patient identifiers (KMS)TLS 1.3
NATS event payloadsTLS 1.3 (NATS mTLS)
Subscription signing keysKMS-managed; never stored plaintext
Idempotency recordsAES-256 at restTLS 1.3

Subscription Security

  • Signed HTTPS payloads: X-Ghasi-Signature: sha256=<hmac> — client verifies before processing.
  • Endpoint URL verified (HTTPS mandatory; health-check on registration).
  • Replay protection: payload includes X-Ghasi-Delivery-Id; consumers deduplicate.
  • Subscription keys rotate per tenant policy (configurable; default: 90 days).

Audit Events (HIPAA-equivalent)

All events emitted to audit-service with tenantId, actorId, resourceType, resourceId, action, correlationId, prescriptionBusinessId, timestamp.

EventTrigger
eprescribing.mr.createdMR persisted
eprescribing.mr.updatedMR status/fields changed
eprescribing.mr.cancelledMR cancelled
eprescribing.md.createdMD created
eprescribing.md.updatedMD updated
eprescribing.search.executedSearch query (patient-scoped)
eprescribing.auth.forbidden403 returned (wrong persona or tenant)
eprescribing.validation.failedIG validation failure
eprescribing.subscription.registeredNew Subscription
eprescribing.subscription.delivery_failedDLQ enqueue
eprescribing.replay.triggeredAdmin replay

Rate Limiting

  • Per-tenant rate limits enforced at Kong + application layer.
  • Configurable per tenant: default POST /fhir/MedicationRequest = 100 req/min.
  • Emergency priority queue available only when tenant policy explicitly enables it (BR-RX-004).
  • 429 RATE_LIMIT_EXCEEDED with Retry-After header.
  • Patient consent flags from registration-service are honored on patient-scoped searches.
  • Break-glass access for emergency prescribing is configurable per tenant policy with full audit.
  • Cross-border data transfers: tenant-declared residency enforced; no cross-region query.

GDPR / Data Privacy

AspectHandling
PHI in transitTLS 1.3 + HMAC-signed Subscription payloads
PHI at restKMS encryption; gepgw_* tables
Data minimizationSearches scoped to authenticated tenant + patient
RetentionClinical records: 10 years; ops events: 90 days; legal hold configurable
ErasureClinical hold applies; erasure queued post-retention; entered_in_error FHIR pattern
DSARPatient medication history exportable via authorized export flow

Security Testing Requirements

  • Adversarial cross-tenant queries in CI (tenant-isolation.integration.spec.ts) — mandatory gate.
  • Persona enforcement: test EHR-backend writing MD returns 403; pharmacy-backend writing MR returns 403.
  • Subscription signature verification test in CI.
  • Quarterly penetration test covering authentication bypass and FHIR injection.