Care Plan Service — Security Model
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD
Authentication
- All endpoints require a valid Keycloak-issued JWT (
Authorization: Bearer). tenantIdis extracted exclusively from the JWTtenant_idclaim — client-suppliedtenantIdin body/query is ignored.- Service-to-service calls (e.g., from patient-chart-service) use client credentials flow with scoped service account tokens.
RBAC Matrix
| Role | care_plan:read | care_plan:write | care_plan:admin | Notes |
|---|---|---|---|---|
clinician | ✅ | ✅ | ❌ | Can create/update plans and goals |
care_coordinator | ✅ | ✅ | ❌ | Full care team coordination rights |
nurse | ✅ | ✅ | ❌ | Can complete activities |
patient_portal_user | ✅ (own plans only) | ❌ | ❌ | Read via FHIR surface, constrained |
admin | ✅ | ✅ | ✅ | Can close/revoke plans |
service_account (internal) | ✅ | ❌ | ❌ | Used by chart and portal services |
ABAC Rules
- A
patient_portal_usercan only read care plans wherepatientIdmatches their own linked patient identity. - A
clinicianwith restricted program access (e.g., HIV/mental health) only sees care plans in their program scope. - Sensitive care plan categories (mental health, HIV, substance abuse) require an additional
sensitive_program:readscope.
Encryption Classes
| Data | At rest | In transit |
|---|---|---|
| Care plan records (Postgres) | AES-256 via cloud KMS (tenant-level key) | TLS 1.3 |
| Outbox payloads | AES-256 same KMS | TLS 1.3 |
| NATS event payloads | Encrypted in transit | TLS 1.3 (NATS mTLS) |
| FHIR read surface | Served via fhir-gateway (TLS at edge) | TLS 1.3 |
Audit Events
All events emitted to the audit-service pipeline:
| Event | Trigger | PHI fields included |
|---|---|---|
care_plan.read | Any GET of a care plan | carePlanId, patientId, actorId, tenantId |
care_plan.created | Successful POST | carePlanId, patientId, actorId |
care_plan.updated | Successful PATCH/status change | carePlanId, previousStatus, newStatus |
care_plan.reviewed | Review workflow | carePlanId, reviewedBy |
care_plan.closed | Plan completed or revoked | carePlanId, closeReason, actorId |
care_plan.goal.updated | Goal status change | goalId, carePlanId, actorId |
care_plan.activity.completed | Activity completed | activityId, carePlanId, actorId |
care_plan.auth_failure | 403 returned | actorId, tenantId, requestedResource |
GDPR / Data Privacy Participation
| Aspect | Handling |
|---|---|
| Data subject access (DSAR) | Care plans are PHI — included in patient data export. Export triggered via patient-portal-service or admin tooling. |
| Right to erasure | Clinical and legal retention holds apply. Erasure requests are queued; executed only after retention period per tenant configuration. |
| Data minimization | API responses scoped to patient context only; no bulk exports without bulk_export:admin scope. |
| Consent enforcement | Sensitive plan categories gated by sensitive_program:read scope; consent flags from registration-service honored. |
| Cross-border | tenant_id maps to a data residency region; Postgres instances deployed per-region; no cross-region query. |
Data Residency
- All Postgres data stays in the tenant's declared residency region.
- NATS events include
tenantId; consumers enforce per-tenant stream isolation. - No care plan data written to multi-region shared storage without explicit tenant consent.