Claims Service — Security Model
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: SERVICE_OVERVIEW · Service Template · 02 DDD
Authentication and Authorization
All API requests must present a valid JWT issued by the tenant's Keycloak realm. The tenant_id claim in the JWT is validated against the X-Tenant-ID header and used to set the Postgres app.tenant_id session variable for RLS enforcement.
RBAC Matrix
| Role | Create Claim | Submit Claim | View Claim | Create Coverage | Verify Eligibility | Process ERA | File Appeal | Admin |
|---|---|---|---|---|---|---|---|---|
billing-staff | Yes | Yes | Yes | Yes | Yes | No | Yes | No |
billing-manager | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No |
registration-staff | No | No | No | Yes | Yes | No | No | No |
clinician | No | No | Read-own | No | No | No | No | No |
patient-portal | No | No | Read-own EOB | No | No | No | No | No |
billing-service (service account) | Yes | Yes | Yes | No | No | No | No | No |
platform-admin | No | No | Yes (audit) | No | No | No | No | Yes |
Notes:
patient-portalrole may only readExplanationOfBenefitandCoveragerecords for the authenticated patient. No claim detail is exposed.- Service accounts use client credentials OAuth2 flow with
subclaim identifying the service.
Module Entitlement
Write operations (POST, PATCH, PUT, DELETE) on all claims and coverage endpoints return 403 MODULE_NOT_LICENSED when the tenant lacks the ehr.claims entitlement in their license record. Read operations (GET) for existing data are permitted to avoid data loss on license expiry.
Tenant Isolation via RLS
All tables (claims, coverages, eligibility_transactions, prior_authorizations, remittances, denial_cases) have:
tenant_id TEXT NOT NULLcolumn- Row-Level Security policy:
USING (tenant_id = current_setting('app.tenant_id')) app.tenant_idis set at connection/transaction start from the validated JWT claim
Cross-tenant access is architecturally impossible via the application layer. Direct DB access is restricted to the service's dedicated Postgres role.
Encryption
| Data Class | At Rest | In Transit |
|---|---|---|
| Claim financial data | AES-256 (Postgres encrypted volume) | TLS 1.3 |
| Coverage / PII | AES-256 | TLS 1.3 |
| Raw EDI payloads (X12 835/837) | AES-256 | TLS 1.3 |
| Payer API credentials | Vault (HashiCorp) — never in application config | TLS 1.3 |
| NATS event payloads | TLS 1.3 in transit; encrypted at rest on JetStream storage | TLS 1.3 |
Payer credentials (API keys, EDI sender IDs, clearinghouse credentials) are stored in HashiCorp Vault and injected at runtime via the Vault agent sidecar.
Audit Events
All PHI-touching mutations emit an audit record to audit-service.
| Operation | Audit Event |
|---|---|
| Create claim | claims.claim.created with actor, patientId, claimId |
| Submit claim | claims.claim.submitted with channel, clearinghouseRef |
| View claim (billing staff) | claims.claim.read with actorId |
| View EOB (patient portal) | claims.eob.read with patientId, actorId |
| Create coverage | claims.coverage.created with actorId, patientId |
| Update coverage | claims.coverage.updated with changed fields |
| Process ERA | claims.remittance.applied with remittanceId, claimsAffected count |
| File appeal | claims.denial.appealed with actorId, denialCode |
Audit records are immutable and retained for 7 years per healthcare regulatory requirements.
EDI Security
- X12 837/835 files transmitted over SFTP+TLS or HTTPS to clearinghouse
- Payer REST API calls use OAuth2 client credentials or API key stored in Vault
- Inbound ERA payloads validated for structure before processing (reject malformed files)
- No clearinghouse credentials stored in application database or source control
GDPR / Data Subject Rights
The claims-service holds financial PHI. Data subject access requests (export) and erasure requests are handled via the platform-admin service. Claims data cannot be erased if it is subject to healthcare retention requirements (minimum 7 years); a legal hold flag prevents deletion and instead marks records as restricted.
Penetration Testing
A security penetration test targeting:
- Cross-tenant RLS bypass attempts
- RBAC privilege escalation
- Payer credential exposure
- EDI payload injection
is required before production launch of the ehr.claims licensed module.