# Plane A — Authorization: prior knowledge and best practices > Research artefact — 2026-08-17. Supports `canon/architecture/adr-008-multi-tenancy-model.md` > (draft-2), plane A of four. Surveys external practice for binding a request > to the tenants it may act for, and holds our `A0–A3` ladder against it. > Companions: plane I (identity), plane E (enforcement), plane P (placement). ## 1. What the plane covers How a request is bound to the tenants its principal may act for, where that decision is made, and how the decision point is kept honest. Our ladder: | Level | State | |---|---| | A0 | None, or tenant context not carried | | A1 | Ad-hoc checks scattered through handlers | | A2 | Single local authorization boundary; context bound once | | A3 | Delegated to `flex-auth` as PDP, live re-query where the IAM Profile requires | ## 2. This is the industry's #1 vulnerability class, by name The single most important external finding: what our framework calls a tenant boundary failure, the security industry calls **Broken Object Level Authorization**, and it is not a marginal concern. - BOLA is **API1** in the OWASP API Security Top 10 and "has held the top spot since the list launched" ([Decryption Digest — BOLA and IDOR testing guide](https://www.decryptiondigest.com/blog/bola-idor-api-authorization-test-remediation)). - It is "consistently the most common, the most exploitable, and the most damaging API vulnerability across bug bounty submissions, public disclosures, and production penetration tests" (ibid.). - The multi-tenant framing is explicit: "a single missing authorization check can let one tenant access another's storage buckets, database records, or configuration data" ([Wiz — OWASP API security top 10](https://www.wiz.io/academy/api-security/owasp-api-security)). **Against our model:** ADR-008 §13.2 identifies the missing cross-tenant evidence artifact as the framework's largest gap. This research upgrades that from an internal judgement to an industry consensus: we have no test coverage for the vulnerability class that ranks first globally. The ADR should cite BOLA/API1 by name so the gap is legible to anyone who arrives with a security background rather than an architecture one. ## 3. OWASP has a Multi-Tenant Security Cheat Sheet, and we should adopt it wholesale This is the most directly applicable artefact found in the whole research effort, and neither ADR-008 nor any repo document references it ([OWASP Multi-Tenant Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html)). Its normative requirements, mapped to our ladders: | OWASP requirement | Our plane | Status | |---|---|---| | "Never trust client-supplied tenant IDs without validation" — derive from verified JWT claims | I2 | Implied, not stated. Tighten. | | "Establish tenant context early in the request lifecycle (middleware/interceptor)"; bind to the authenticated session | A2 | **Matches A2 exactly.** | | "Always validate that requested resources belong to the current tenant" | E1–E2 | Matches. | | "Include tenant context in all log entries" | — | **Not covered by any plane.** | | Tenant-isolated audit trails | — | `audit-core`'s concern; not in the framework. | | "Ensure complete data deletion on tenant offboarding" | — | **Not covered. See §6.** | | Prefix all cache keys with tenant identifiers | — | **Not covered** (see plane I §3). | | Per-tenant rate limiting with tier-based quotas | — | Adjacent to `tenant-engine` guardrails. | | Separate API keys per tenant | E4-adjacent | Our E4 is credential-based; consistent. | Explicitly rejected anti-patterns, two of which we should check ourselves against: - **"Skipping tenant validation for internal services."** Our estate is mostly internal service-to-service calls — `flex-auth` calls `tenant-engine` synchronously on the authorization path, `user-engine` calls it for tenant creation. Our A ladder describes a request arriving with a subject; it does not say whether a *service* identity calling on behalf of a tenant carries and revalidates tenant context. This is a real question for A3. - **"Exposing sequential or guessable resource IDs."** Not assessed anywhere. **Proposed action:** reference the cheat sheet from ADR-008 as the external baseline, and fold its uncovered requirements (logging, cache keys, offboarding deletion) into either the ladders or an explicit scope exclusion. ## 4. The PDP/PEP split is right, and there is now a standard for the wire Our A3 delegates decisions to `flex-auth` as PDP, which is the mainstream architecture — a Policy Enforcement Point intercepts an action and queries a Policy Decision Point ([DEV — RBAC vs ABAC vs ReBAC](https://dev.to/kanywst/rbac-vs-abac-vs-rebac-how-to-choose-and-implement-access-control-models-3i2d)). What is new since our contracts were written: - **OpenID AuthZEN Authorization API 1.0** was approved as an OpenID Final Specification in January 2026 and published Standards Track in March 2026 ([OpenID Foundation — Authorization API 1.0 final specification approved](https://openid.net/authorization-api-1-0-final-specification-approved/), [spec](https://openid.github.io/authzen/)). - Its purpose is exactly our seam: letting PDPs and PEPs "communicate authorization requests and decisions to each other without requiring knowledge of each other's inner workings" ([Curity — introduction to AuthZEN](https://curity.io/resources/learn/authzen/)). - Interoperability is demonstrated, not theoretical: one PEP implementation driving 5+ different PDPs (Topaz, Axiomatics, OpenFGA) by switching endpoint URLs alone ([DEV — AuthZEN deep dive](https://dev.to/kanywst/authzen-authorization-api-10-deep-dive-the-standard-api-that-separates-authorization-decisions-1m2a)). - Keycloak shipped experimental AuthZEN support in May 2026 ([Keycloak — AuthZEN as experimental feature](https://www.keycloak.org/2026/05/authzen-as-experimental-feature)). **Against our model:** `flex-auth` uses a bespoke `CheckRequest` shape and a bespoke action vocabulary (`tenant.update`, `tenant.guardrail.set`, …), with action strings copied verbatim between repos to avoid re-derivation. That coupling is exactly what AuthZEN standardises away. We are not wrong — we are pre-standard, and the standard landed seven months ago. **Proposed amendment:** A3 currently says "delegated to `flex-auth` as PDP". Consider distinguishing: - **A3** — externalised to a PDP (any protocol). - **A4** — externalised over a standard interface (AuthZEN), so the PDP is swappable and the PEP is not coupled to one engine's request shape. That gives the ladder somewhere to go and makes the current coupling visible rather than invisible. It also reframes `key-cape`/Keycloak's AuthZEN support as a fleet opportunity rather than an unrelated upstream feature. ## 5. Model choice: our vocabulary is RBAC-shaped, our problem may be ReBAC-shaped - ReBAC originates in Google's **Zanzibar**, modelling authorization as a graph of typed relationships, with authorization data stored as explicit tuples in a dedicated datastore ([AuthZed — introduction to Google Zanzibar](https://authzed.com/learn/google-zanzibar)). - It is described as "well-suited to multi-tenant enterprise environments where access patterns are relational rather than role-based", and specifically for modelling "complex customer organizational structures while maintaining strict data isolation" (ibid., [AuthZed — ABAC vs ReBAC](https://authzed.com/learn/abac-vs-rebac-when-to-use-which)). - Open implementations: SpiceDB, OpenFGA, Authzed ([IAM Day by Day — Zanzibar & ReBAC](https://iamdaybyday.com/patterns/authorization/zanzibar/)). **Against our model:** our capability roles (`PLTF`/`IAM`/`VEN`/`CUS`) are tenant-level RBAC, and `user-engine` owns memberships separately. The moment a question like "may this user act for this tenant, given they are a member of an organisation that administers it" arises, that is a relationship query, and role-flattening will start to strain. Not a present problem — flagged because the estate's own tenant taxonomy (`family`, `community`, `association`) is overtly relational and will produce these questions. ## 6. Finding: nothing in the framework covers tenant exit OWASP lists "ensure complete data deletion on tenant offboarding" as a **must** ([OWASP Multi-Tenant Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html)). Our position today: `tenant-engine`'s lifecycle contract provides reversible retirement and states plainly that **there is no hard-delete**. That is a defensible design for an audit-bearing estate, but it means: - No plane in ADR-008 has anything to say about tenant exit, data deletion, or the interaction with retention. - Physical backups on `platform-pg` retain 30 days, so even a hypothetical hard-delete leaves tenant data recoverable for a month — which is a GDPR erasure question, not just an architecture one. - `rapp-postgres` SCOPE explicitly excludes tenant identity, and `tenant-engine` explicitly excludes storage. Deletion falls between them. **Proposed action:** this is a fifth concern the framework does not model, and it is not obviously a fifth *plane* — it is a lifecycle stage cutting across all four. Recommend adding it as an explicit open question rather than inventing a plane for it. The estate's `gdpr_report` tooling in the hub suggests someone has thought about this elsewhere; worth locating before drafting. ## 7. Finding: automated conformance testing may not be sufficient for this plane This challenges the recommendation I gave in the ADR review (§18.3, "build a fleet conformance suite"). - "Finding cross-tenant vulnerabilities requires semantic understanding of what data belongs to which context, and that understanding requires a human tester who can set up separate tenant contexts and compare the responses." - "Across 119 SaaS assessments, the highest-impact findings were concentrated in access control, business logic, and tenant isolation, precisely the categories that require context-aware human testing rather than automated tool execution." ([Bugstrix — multi-tenant SaaS security testing](https://bugstrix.com/blogs/multi-tenant-saas-security-testing-how-to-prevent-cross-tenant-data-leaks/)) **Against our model:** this does not invalidate the conformance suite — a mechanical check that every tenant-owned table carries the key (E1), or that a session without the GUC reads nothing (E3), is precisely the kind of structural assertion automation does well. What it invalidates is the hope that a suite *replaces* adversarial review at E2, where the boundary lives in business logic and the failure is semantic. **Proposed amendment to §13:** split the evidence artifacts by what can be automated. E1 and E3 are mechanical and belong in CI. E2 is semantic and needs a periodic adversarial review with a recorded date, closer to a pen-test cadence than a test suite. Claiming E2 on the strength of a green CI run would be exactly the overclaim §6 of the ADR prohibits. ## 8. Summary against our ladder | Finding | Verdict | |---|---| | Tenant boundary failure = BOLA = OWASP API1, top of the list since launch | **Reframe.** Cite by name; our largest gap is the industry's largest risk. | | OWASP Multi-Tenant Security Cheat Sheet exists and is unreferenced | **Adopt.** Closest external baseline to our framework. | | A2 matches OWASP's "establish context early, bind to session" | **Aligned.** | | "Skipping tenant validation for internal services" is a named anti-pattern | **Check ourselves.** Our estate is mostly internal service-to-service. | | PDP/PEP split is mainstream | **Aligned.** | | AuthZEN 1.0 Final (Jan 2026) standardises the PDP wire; Keycloak experimental May 2026 | **Gap.** `flex-auth` is pre-standard and bespoke. Propose A4. | | ReBAC/Zanzibar suits relational tenant structures | **Watch.** Our `family`/`community`/`association` groupings are relational. | | Tenant offboarding deletion is a MUST we do not model | **Finding.** No plane covers exit; interacts with 30-day backup retention. | | Cross-tenant testing needs human semantic review, not only automation | **Amend §13.** E1/E3 mechanical, E2 adversarial-review with a date. | ## Sources - [OWASP — Multi-Tenant Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html) - [Wiz — OWASP API Security Top 10 risks and how to mitigate them](https://www.wiz.io/academy/api-security/owasp-api-security) - [Decryption Digest — BOLA and IDOR testing guide](https://www.decryptiondigest.com/blog/bola-idor-api-authorization-test-remediation) - [SecPortal — Broken Object Level Authorization (BOLA) guide](https://secportal.io/vulnerabilities/broken-object-level-authorization) - [Bugstrix — Multi-tenant SaaS security testing: preventing cross-tenant data leaks](https://bugstrix.com/blogs/multi-tenant-saas-security-testing-how-to-prevent-cross-tenant-data-leaks/) - [OpenID Foundation — Authorization API 1.0 Final Specification approved](https://openid.net/authorization-api-1-0-final-specification-approved/) - [OpenID AuthZEN — Authorization API 1.0 specification](https://openid.github.io/authzen/) - [OpenID Foundation — AuthZEN working group specifications](https://openid.net/wg/authzen/specifications/) - [Curity — An introduction to Authorization Exchange (AuthZEN)](https://curity.io/resources/learn/authzen/) - [DEV — AuthZEN Authorization API 1.0 deep dive](https://dev.to/kanywst/authzen-authorization-api-10-deep-dive-the-standard-api-that-separates-authorization-decisions-1m2a) - [Keycloak — AuthZEN as an experimental feature (May 2026)](https://www.keycloak.org/2026/05/authzen-as-experimental-feature) - [Auth0 — A guide to OpenID AuthZEN's Authorization API 1.0](https://auth0.com/blog/implementing-authzen-guide-openid-authorization-api/) - [AuthZed — An introduction to Google Zanzibar and ReBAC](https://authzed.com/learn/google-zanzibar) - [AuthZed — ABAC vs ReBAC: when to use which](https://authzed.com/learn/abac-vs-rebac-when-to-use-which) - [IAM Day by Day — Zanzibar & relationship-based access control](https://iamdaybyday.com/patterns/authorization/zanzibar/) - [DEV — RBAC vs ABAC vs ReBAC: how to choose](https://dev.to/kanywst/rbac-vs-abac-vs-rebac-how-to-choose-and-implement-access-control-models-3i2d)