Published 2026-11-05 · By Jeffrey Monica
Authentication is not authorization
A valid session only identifies a caller. It does not establish permission to act on an arbitrary account, file, invoice or tenant. Broken Object Level Authorization (BOLA) happens when an endpoint looks up a user-selected object without enforcing the caller's permission for that object and action. UUID identifiers do not fix the issue; neither does hiding a button in an iOS or Android interface.
Define the requested resource, acting subject, tenant boundary, action and delegation rules before writing policy code. A route such as GET /api/accounts/{accountId}/documents/{documentId} crosses at least two client-controlled object boundaries. Verify the relationship to the authenticated principal at the data access boundary.
Move enforcement next to the read or mutation
Central middleware should authenticate callers, but authorization decisions frequently need record attributes unavailable at the outer HTTP boundary. Use a service policy function that receives the verified principal, target object, tenant and requested action. Scope database queries by authorized tenant or ownership where possible, and recheck permission before any write. Never trust X-Tenant-ID, userId or role values merely because they appear in a signed-in client's payload.
Async jobs, export links, caches, internal RPCs and administrative endpoints need the same enforcement. Beware the common path where an HTTP handler checks authorization, then queues an object ID for a worker that retrieves data under a global service account. Carry authenticated authorization context through the job or re-evaluate permission at execution time.
Validation checklist
- A user can read their own object but not a peer's object.
- A tenant administrator cannot access another tenant's records.
- A changed path ID, nested ID or body field cannot switch the target object.
- Background jobs and exports enforce the same scope as the synchronous API.
Tests that catch regressions
Create at least two users and two tenants in test fixtures. Assert explicit 403 or privacy-preserving 404 behavior for unauthorized reads, writes and deletes; test both existing and nonexistent object IDs. Include list endpoints, count endpoints and exports because data exposure can happen without a single-object read.
Test role downgrade, revoked access, cached responses, batch APIs and eventual-consistency windows. If the backend computes a permission decision from claims that can outlive revocation, define the maximum stale authorization window and test it.
Operational evidence
Record policy outcomes using stable reason codes, subject/tenant context and request IDs with privacy controls; do not log secrets or full records. Review unusual denial patterns as a signal, but do not equate one rejected request with an intrusion. Security review findings are useful only when the corrected invariant becomes a regression test and an owner maintains it.
Runnable verification example
An original public lab demonstrates a cross-tenant document request before and after object-level authorization, with synthetic data and a local-only Node.js server.
git clone https://github.com/DemonicBinary/api-authorization-lab.git cd api-authorization-lab npm test npm startRun the interactive walkthrough
Primary references
Engineering practices and threat-model choices, not product certification, a guarantee against compromise or a customer penetration-test result.
