Access Control Integration & Enforment Contract
Status
Section titled “Status”This document is FINAL and ENFORCEABLE.
All backends must implement access control exactly as defined here.
Any deviation must be explicitly approved and documented.
Purpose
Section titled “Purpose”This document defines the final integration and enforcement contract between:
- Auth Backend
- Platform Core Backend
- Base Backend
- Module Backends
- Frontend
It exists to remove ambiguity before implementation.
This document does not replace:
architecture/documentation.md2.1.-Backend-Auth.md2.2.-Backend-Core.md2.2.1.-Backend-Core-API.md2.3.-Backend-Base.md
It connects them.
1. Core principle
Section titled “1. Core principle”The platform access rule is:
effective_access = company_entitlements ∩ membership_grantsWhere:
company_entitlements= company commercial ownership from Platform Coremembership_grants= user/company access assignments from Autheffective_access= final merged access result computed by Auth- enforcement = done by Base / Module Backends
2. Final responsibility split
Section titled “2. Final responsibility split”2.1 Auth Backend owns
Section titled “2.1 Auth Backend owns”Auth is the source of truth for:
- users
- identity
- login
- JWT issuance
- sessions
- refresh token lifecycle
- company memberships
- tenant roles
- module grants per membership
- permissions per membership
- delegation rules
- invitations
- effective access computation
2.2 Platform Core Backend owns
Section titled “2.2 Platform Core Backend owns”Platform Core is the source of truth for:
- packages
- add-ons
- subscriptions
- module catalog
- package → module mapping
- add-on → module mapping
- company entitlements
- company enabled modules
- entitlement versioning
2.3 Base Backend owns
Section titled “2.3 Base Backend owns”Base backend owns:
- business logic
- business routes
- business data reads/writes
- enforcement of effective access returned by Auth
Base backend does not own:
- identity
- company entitlements
- membership truth
- permissions truth
- package/subscription truth
- access computation
2.4 Module Backends own
Section titled “2.4 Module Backends own”Each module backend owns its own business data and business execution.
Examples:
- Finance backend → finance business records
- Market backend → market business records
- Venue backend → venue business records
- Touring backend → touring business records
- AI backend → AI outputs and AI business flows
Module backends do not own:
- user identity
- package truth
- entitlement truth
- final access computation
2.5 Frontend owns
Section titled “2.5 Frontend owns”Frontend owns:
- UX decisions
- module visibility
- route visibility
- local state for rendering
Frontend does not own:
- final access truth
- security enforcement
- package truth
- permission truth
Frontend is never the source of truth.
3. Access model layers
Section titled “3. Access model layers”This platform uses a 3-layer access model.
3.1 Level 1 — Company entitlements
Section titled “3.1 Level 1 — Company entitlements”Question answered:
Did the company buy this module?
Owned by:
- Platform Core
Examples:
- company has Basic
- company has Finance
- company has Market
- company does not have AI
If the company did not buy a module, no user in that company may use it.
This is the upper bound.
3.2 Level 2 — Membership module grants
Section titled “3.2 Level 2 — Membership module grants”Question answered:
Inside the modules the company owns, which modules can this membership access?
Owned by:
- Auth
Examples:
- company bought Basic + Finance + Market
- membership granted only Finance
Then that membership only gets Finance.
3.3 Level 3 — Fine-grained permissions
Section titled “3.3 Level 3 — Fine-grained permissions”Question answered:
Inside a module the user can access, what exactly can the user do?
Owned by:
- Auth
Examples:
basic.event.viewbasic.event.createfinance.expense.viewfinance.expense.editmarket.contract.approve
Permissions are valid only if the parent module is valid in effective access.
4. Roles vs modules vs permissions
Section titled “4. Roles vs modules vs permissions”These concepts must never be confused.
4.1 Role
Section titled “4.1 Role”Role defines:
- administrative authority
- management scope
- delegation authority
Examples:
TENANT_SUPERADMINADMINMANAGERUSERSUBMITTER
Role does not automatically define which modules the user can access.
4.2 Module grant
Section titled “4.2 Module grant”Module grant defines:
- product access inside one company
Examples:
- Basic only
- Finance only
- Basic + Market
- Finance + Market
A user can be USER and still have Finance only.
A different USER can have Basic + Market.
That is valid.
4.3 Permission
Section titled “4.3 Permission”Permission defines:
- what a user can do inside an enabled module
Examples:
- view
- create
- edit
- delete
- approve
- export
- manage
4.4 Critical clarification
Section titled “4.4 Critical clarification”Role = authorityModule = product accessPermission = action accessThese are separate layers.
5. Delegation model
Section titled “5. Delegation model”Tenant roles define what a user can grant to others.
5.1 TENANT_SUPERADMIN
Section titled “5.1 TENANT_SUPERADMIN”- has access to all modules the company owns
- may assign module access to others
- may assign permissions to others
- may define delegation boundaries for lower roles
- may buy add-ons / manage subscriptions
Still limited by company entitlements.
If company did not buy AI, even TENANT_SUPERADMIN cannot use or grant AI.
5.2 ADMIN
Section titled “5.2 ADMIN”- operational company administrator
- may manage users
- may assign module access
- may assign permissions
- may not buy add-ons
- may not grant beyond what Superadmin delegated
This must be enforced by delegation policy, not only by role name.
5.3 MANAGER / APPROVAL
Section titled “5.3 MANAGER / APPROVAL”- limited delegation role
- may manage users below them only within delegated scope
- may not buy add-ons
- may not exceed what Admin/Superadmin delegated
5.4 USER / SUBMITTER
Section titled “5.4 USER / SUBMITTER”- no delegation by default
- only receives granted modules and permissions
- normal consuming user
5.5 Delegation rule
Section titled “5.5 Delegation rule”A grant action is valid only if all are true:
- actor role allows that kind of grant
- actor delegation policy allows that target role / module / permission
- company owns the module
- actor is authorized in the active company
- actor is not exceeding delegated scope
Delegation belongs only to Auth.
Base and Core do not enforce delegation policy as a source of truth.
6. Base → Auth runtime contract
Section titled “6. Base → Auth runtime contract”6.1 Recommended endpoint
Section titled “6.1 Recommended endpoint”Base and module backends must resolve effective access from Auth.
Recommended contract:
GET /auth/me/accessThis is the canonical access resolution endpoint.
6.2 Required request context
Section titled “6.2 Required request context”Base / module backends must preserve user context and company context.
Required user context
Section titled “Required user context”Authorization: Bearer <USER_ACCESS_TOKEN>Required company context
Section titled “Required company context”x-org: <COMPANY_ID>Required service-to-service auth
Section titled “Required service-to-service auth”Choose one:
X-Internal-API-Key: <SERVICE_KEY>or mTLS / internal service token if your infra uses that.
6.3 Wire-shape rule
Section titled “6.3 Wire-shape rule”The system must not leave ambiguity about this call.
At implementation time, the Auth contract must clearly define whether Base uses:
Option A — preferred
Section titled “Option A — preferred”same GET /auth/me/access endpoint with:
- user bearer token
x-org- internal service credential
Option B
Section titled “Option B”dedicated internal route such as:
GET /internal/auth/accesswith:
- user bearer token
x-org- internal service credential
The contract must be explicit in OpenAPI / API docs before implementation.
6.4 Recommended behavior
Section titled “6.4 Recommended behavior”Auth must:
- validate user JWT
- validate service credential
- validate
x-org - resolve membership
- call Core for entitlements
- merge effective access
- return normalized access result
7. Required Auth effective access response
Section titled “7. Required Auth effective access response”The exact full response may vary by product, but Auth must return enough information for enforcement.
Minimum recommended shape:
{ "userId": "uuid", "companyId": "uuid", "tokenVersion": 1, "entitlementVersion": 7, "tenantRole": "ADMIN", "modules": ["basic", "finance"], "permissions": [ "basic.event.view", "finance.expense.view" ], "delegation": { "canManageUsers": true, "canBuyAddons": false, "grantableModules": ["basic"], "grantablePermissions": [ "basic.event.view" ] }}7.1 Mandatory fields
Section titled “7.1 Mandatory fields”At minimum, Base/backend enforcement needs:
userIdcompanyIdtokenVersionmodulespermissions
Strongly recommended:
tenantRoleentitlementVersiondelegation
7.2 Membership requirement
Section titled “7.2 Membership requirement”Auth must not compute effective access unless membership exists.
Membership is created through:
- invitation acceptance
- internal admin actions
- company membership upserts in Auth
Without valid membership:
effective access must not be computedResult should be deny / forbidden according to route context.
8. Base / module middleware flow (MANDATORY)
Section titled “8. Base / module middleware flow (MANDATORY)”Every tenant-scoped business request must follow this flow.
8.1 Step 1 — Read request headers
Section titled “8.1 Step 1 — Read request headers”Read:
Authorizationx-org
If route is tenant-scoped and x-org missing:
- reject request
8.2 Step 2 — Validate JWT
Section titled “8.2 Step 2 — Validate JWT”Validate:
- signature
- expiration
- issuer
- audience
- token format
If invalid:
- return
401
Base/module backends must not issue or refresh tokens.
8.3 Step 3 — Validate company context
Section titled “8.3 Step 3 — Validate company context”Validate:
x-orgis presentx-orgis in valid canonical format- tenant route has explicit company context
If invalid/missing:
- return
400
Base must not invent default company silently.
8.4 Step 4 — Resolve effective access
Section titled “8.4 Step 4 — Resolve effective access”Call Auth.
If Auth resolution fails:
- return
503
No local access guess is allowed.
8.5 Step 5 — Check module access
Section titled “8.5 Step 5 — Check module access”Confirm required module exists in effective modules.
Examples:
basicfinancemarkettouringvenueai
If missing:
- return
403
8.6 Step 6 — Check permission access
Section titled “8.6 Step 6 — Check permission access”Confirm required permission exists.
Examples:
basic.event.viewfinance.expense.viewmarket.contract.approve
If missing:
- return
403
8.7 Step 7 — Execute route
Section titled “8.7 Step 7 — Execute route”Only after successful checks.
8.8 Final middleware summary
Section titled “8.8 Final middleware summary”JWT validation→ x-org validation→ Auth effective access→ module check→ permission check→ execute or deny9. Mandatory enforcement policy
Section titled “9. Mandatory enforcement policy”All endpoints in Base and module backends must follow this rule.
9.1 Required flow
Section titled “9.1 Required flow”For every protected request:
- validate JWT
- validate
x-org(tenant routes) - resolve effective access from Auth
- verify module
- verify permission
- execute or deny
9.2 Hard requirements
Section titled “9.2 Hard requirements”A request MUST be rejected if:
- JWT is missing or invalid →
401 x-orgis missing on tenant route →400- user does not belong to company →
403 - required module missing →
403 - required permission missing →
403 - effective access cannot be resolved →
503
9.3 Failure examples for 503
Section titled “9.3 Failure examples for 503”This typically includes:
- Auth service unavailable
- timeout calling Auth
- network failure between backend and Auth
- Auth failing because Core is unavailable
- corrupted upstream response during access resolution
In all such cases, fail closed.
9.4 Strict rules
Section titled “9.4 Strict rules”Backends must NEVER:
- allow access based on old package logic
- allow access based on old subscription logic
- allow access based on local permission tables as source of truth
- allow access based on local role assumptions
- infer access from JWT alone
- trust frontend visibility as authorization
10. Security principle — fail closed
Section titled “10. Security principle — fail closed”The platform must use a strict fail-closed model.
If any of the following cannot be verified:
- JWT validity
- company context
- membership
- effective access
- permission presence
Then the request MUST be denied.
Under no circumstance should the backend:
- guess access
- fallback to optimistic assumptions
- allow temporary bypass
- trust stale UX state as authority
11. Platform Core dependency rule
Section titled “11. Platform Core dependency rule”Auth depends on Core for company entitlements.
11.1 Runtime chain
Section titled “11.1 Runtime chain”Frontend → Base/Module Backend → Auth → Coreor for frontend bootstrap:
Frontend → Auth → Core11.2 Core failure behavior
Section titled “11.2 Core failure behavior”If Core cannot provide entitlements:
- Auth must fail access resolution
- Base/module backend must fail request
- response must be
503
No degraded privilege mode by default.
11.3 No direct Core authorization
Section titled “11.3 No direct Core authorization”Base/module backends must not call Core directly for final user authorization.
Core only provides:
- company commercial state
- enabled modules
- entitlement version
Auth merges that with membership grants and permissions.
12. x-org contract
Section titled “12. x-org contract”12.1 Meaning
Section titled “12.1 Meaning”x-org = canonical company id
It identifies the active company context for a tenant-scoped request.
12.2 Rules
Section titled “12.2 Rules”x-org:
- must be present on tenant routes
- must not be guessed
- must not silently fallback in Base/module backends
- must use canonical company id format everywhere
12.3 Migration requirement
Section titled “12.3 Migration requirement”If legacy company identifiers exist:
- normalize them before authorization logic
- avoid mixed Mongo
_idvs UUID usage inside enforcement
Target state:
one canonical company id everywhere12.4 Base vs frontend behavior
Section titled “12.4 Base vs frontend behavior”Base/module backends:
- always require explicit
x-orgfor tenant routes
Frontend/Auth bootstrap:
- may implement company selection UX
- may store last selected company in client state
But that selection must become explicit x-org on business requests.
13. Access cache rules
Section titled “13. Access cache rules”Caching is allowed only as a performance optimization.
It must never become the source of truth.
13.1 Recommended cache key
Section titled “13.1 Recommended cache key”access:{userId}:{companyId}:{tokenVersion}Strongly recommended richer key or stale-check:
access:{companyId}:{membershipId}:{accessVersion}:{entitlementVersion}13.2 Required validation inputs
Section titled “13.2 Required validation inputs”Cached access must be considered valid only if all required inputs still match current context.
Minimum checks:
- userId matches
- companyId matches
- tokenVersion matches
Strongly recommended:
- accessVersion matches
- entitlementVersion matches
13.3 Invalidation triggers
Section titled “13.3 Invalidation triggers”Invalidate cached access when any of these change:
- login
- refresh
- tokenVersion change
- membership change
- module grant change
- permission change
- delegation policy change
- company subscription change
- company add-on change
- entitlementVersion change
13.4 Critical cache rule
Section titled “13.4 Critical cache rule”If Auth is unavailable:
DO NOT USE STALE CACHE TO ALLOW REQUESTSReturn 503.
Fail closed.
13.5 TTL guidance
Section titled “13.5 TTL guidance”TTL may be short-lived, for example:
- 30 seconds
- 60 seconds
- 120 seconds
TTL improves performance but is not a correctness mechanism.
Correctness depends on invalidation.
14. Access must never be inferred from JWT alone
Section titled “14. Access must never be inferred from JWT alone”JWT proves identity and session context.
JWT does not contain authoritative access state.
JWT must not be used alone to decide:
- module access
- permission access
- company entitlement
- delegation rights
All access decisions must come from Auth effective access resolution.
15. Route access classification
Section titled “15. Route access classification”Every backend route group should be classified.
15.1 Classification matrix
Section titled “15.1 Classification matrix”| Type | Requires JWT | Requires x-org | Uses Auth effective access | Notes |
|---|---|---|---|---|
| Tenant | Yes | Yes | Yes | Standard business routes |
| Vendor | Yes | No | No | Vendor token model |
| Public | No | No | No | Open endpoints |
| Mixed | Yes | Conditional | Conditional | Explicit rules required |
15.2 Tenant routes
Section titled “15.2 Tenant routes”Tenant routes:
- require JWT
- require
x-org - require Auth effective access
- require module and permission enforcement
15.3 Vendor routes
Section titled “15.3 Vendor routes”Vendor routes:
- validate vendor token class
- do not use tenant company membership model unless explicitly designed
- do not assume
x-org
15.4 Public routes
Section titled “15.4 Public routes”Public routes:
- do not require JWT
- do not call Auth for access checks
- are not allowed to expose protected data
15.5 Mixed routes
Section titled “15.5 Mixed routes”Mixed routes must explicitly document:
- allowed principal types
- whether
x-orgis required - whether effective access is required
- whether vendor tokens are allowed
No implicit fallback.
16. Frontend integration rules
Section titled “16. Frontend integration rules”Frontend uses access for UX only.
16.1 Frontend may use Auth access response for:
Section titled “16.1 Frontend may use Auth access response for:”- module cards / app tiles
- route guards
- showing/hiding buttons
- settings pages
- user management screens
16.2 Frontend must not assume:
Section titled “16.2 Frontend must not assume:”- cached access is permanent truth
- local state overrides backend checks
- token contains modules/permissions
- visible UI equals authorized API access
Backends remain the final authority.
16.3 Frontend should refetch access when:
Section titled “16.3 Frontend should refetch access when:”- login completes
- access token refresh completes
- company changes
- app reload happens
- user performs grant/revoke actions
- admin changes company add-ons
- request returns access-changed / forbidden due to updated state
17. Auth already implemented — clarification
Section titled “17. Auth already implemented — clarification”When the docs say Auth is already implemented and should not be changed blindly, this means:
- do not refactor Auth carelessly
- do not break existing contracts unnecessarily
It does not mean:
- Auth is frozen forever
/auth/me/accessmust remain incomplete- Core integration cannot be added
Required work still includes:
- proper Core integration
- correct access merging
- Redis caching/invalidation
- invitation / membership / delegation support
- internal service-to-service contract hardening
18. Required QA scenarios
Section titled “18. Required QA scenarios”QA must validate at minimum:
18.1 Success path
Section titled “18.1 Success path”- valid JWT
- valid
x-org - valid membership
- valid module
- valid permission
- request succeeds
18.2 Authentication failures
Section titled “18.2 Authentication failures”- invalid JWT →
401 - expired JWT →
401 - wrong issuer/audience →
401
18.3 Company context failures
Section titled “18.3 Company context failures”- missing
x-orgon tenant route →400 - malformed
x-org→400
18.4 Authorization failures
Section titled “18.4 Authorization failures”- no membership in company →
403 - module not enabled →
403 - permission not granted →
403
18.5 Upstream failures
Section titled “18.5 Upstream failures”- Auth unavailable →
503 - Core unavailable → Auth fails → backend returns
503
18.6 Cache behavior
Section titled “18.6 Cache behavior”- tokenVersion change invalidates cache
- entitlementVersion change invalidates cache
- stale cache present + Auth unavailable → request denied, not allowed
19. Recommended implementation checklist
Section titled “19. Recommended implementation checklist”-
GET /auth/me/accessfinalized - Core entitlements call integrated
- effective access merge implemented
- delegation included if required
- cache + invalidation implemented
Base Backend
Section titled “Base Backend”- JWT validation middleware
-
x-orgenforcement middleware - Auth access-resolution middleware
- module guard middleware
- permission guard middleware
- fail-closed behavior
Module Backends
Section titled “Module Backends”- same enforcement model as Base
- no direct Core access for authorization
- route classification documented
Frontend
Section titled “Frontend”- access bootstrap after login
- access refresh after company switch
- never trust UI alone
20. Final enforcement rule
Section titled “20. Final enforcement rule”Base / module backends enforceAuth decidesCore provides entitlementsFrontend is not trusted21. Final one-line summary
Section titled “21. Final one-line summary”Company purchase enables modules.Membership grants decide who can use them.Permissions decide what they can do inside them.Roles and delegation decide who can grant access to others.