ODC Architecture Specialist study materials
ODC Architecture Specialist study resources

Tenant Isolation in a Multi-Tenant Healthcare Portal

ODC Architecture Specialist · Put your knowledge to work in context.

All case studies

Tenant Isolation in a Multi-Tenant Healthcare Portal

A healthcare SaaS provider runs a multi-tenant portal on ODC for 120 tenant organizations and 18,000 users. Eighty tenants use B2B SSO through OIDC; forty rely on local ODC accounts. The app has a single TenantId column on patient and appointment entities, and three app roles: Admin, Clinician, and Viewer. A penetration test found that a clinician from Tenant A could change a patientId in a URL and view a patient from Tenant B. The test also found a report server action that accepts tenantId as an input parameter and returns counts for any tenant. Tenant admins currently assign roles by editing a comma-separated list in a site property. The provider wants to keep one codebase and one deployment, add tenant-specific role mapping, and produce audit evidence for access to patient records within 90 days.

1. Identify the authorization flaws and propose an enforceable tenant-isolation model.

Read the worked answer

The flaws are structural, not cosmetic. The app trusts client-supplied patientId and tenantId, so authorization is enforced only by the UI. A single TenantId column is necessary but not sufficient; every read and write must be constrained by the authenticated tenant, and the tenant must come from a trusted claim or session, never from a request parameter. The comma-separated site property is also a bypass: it has no per-tenant validation, no audit, and likely no least privilege.

I would enforce tenant context at the server-action and aggregate layer: a wrapper resolves tenant from the identity token, rejects mismatches, and applies the filter before any query. Roles should be scoped per tenant, with explicit grants for Admin, Clinician, and Viewer, and deny-by-default for unassigned users. For defense in depth, use separate data-access service actions that require a tenant context object and add row-level predicates. Remove tenantId from public contracts. Add negative tests: cross-tenant patientId, cross-tenant report, and role escalation. Audit every access decision with tenant, user, patient, action, and result.

2. How would you support the 40 tenants without an IdP while keeping one codebase and meeting the 90-day audit deadline?

Read the worked answer

Do not fork the app or create a second security model. Use ODC's local authentication for the 40 tenants, but put them behind the same tenant-resolution layer as SSO users. Each local user is provisioned with a tenant membership and tenant-scoped roles; invitations and password reset must verify tenant membership. Require MFA for tenant admins and for any role that can export patient data. For SSO tenants, map OIDC claims to tenant and role groups at login; for local tenants, use an equivalent provisioning table. Keep one codebase by making identity source an attribute, not a branch in business logic.

The 90-day audit requirement is achievable if you prioritize: first remove client-supplied tenantId and add server-side tenant filters, then add tenant-scoped role assignment, then emit audit events for patient record access. A full federation broker for the 40 tenants can follow later if they demand SSO. Document the compensating controls for local accounts: MFA, lockout, session timeout, and quarterly access review.

3. Define the least-privilege and audit requirements for the report server action that accepted tenantId.

Read the worked answer

The report action must not accept tenantId from the caller. It should derive tenant from the authenticated context, check that the caller has a report permission for that tenant, and constrain every query to that tenant. If the report supports cross-tenant benchmarking, that is a separate platform-admin action with a distinct role, explicit justification, and break-glass approval; it must not share the same contract. Apply least privilege to data: aggregate reports should return counts and de-identified fields, not patient names, unless the role explicitly allows identified data. Add field-level masking for Clinician versus Viewer.

Audit requirements: log who ran the report, tenant, parameters excluding secrets, row count, timestamp, and success or denial; store logs immutably and retain them per policy. Alert on repeated cross-tenant attempts or unusual report volume. Add automated tests that a Tenant A clinician receives zero Tenant B rows even if they tamper with hidden fields. Review the report's role assignment quarterly and remove dormant access.