Object-level API authorization: why a valid JWT is not enough
Practical tests for broken object-level authorization in authenticated mobile and backend APIs.
Consider a mobile application with an endpoint for retrieving a saved address. A request arrives with a valid bearer token and a resource identifier chosen by the caller. The token proves something about the caller's session; it does not prove the caller is entitled to see that particular address.
This gap is the basis of OWASP API1:2023 Broken Object Level Authorization, often shortened to BOLA. It's a practical threat because a legitimate user may be able to call the endpoint normally while changing the object ID.
The trust boundary isn't the URL format
A sequential integer makes unauthorized object IDs easy to guess, but switching to UUIDs does not solve authorization. Object IDs can leak through logs, screenshots, shared URLs, error messages, client caches, or other endpoints.
The correct invariant is simple: the server must check that the authenticated principal is entitled to perform the requested action on the specific object. Check it even if the UI would never show that object ID to the user.
That is a review sketch, not an executable endpoint.
Test across object types and operations
If a system exposes addresses, payment methods, account preferences, invoices, team workspaces, or support tickets, each endpoint carrying an object ID deserves an explicit policy review.
| Variation | What to validate |
|---|---|
| Read someone else's object | No unauthorized data disclosed |
| Update another tenant's object | Server rejects before mutation |
| Delete or export another tenant's object | Same authorization enforcement as read/update |
| Change organization or workspace ID | Tenant boundary validated on the server |
| Supply an ID in request body rather than path | Authorization still applies |
| Replay a cached request after role removal | Current permission state enforced |
A successful login only establishes a session. It cannot replace current authorization checks on downstream resources.
Don't mistake integrity for access
A positive App Attest or Play Integrity verdict still does not establish that an account owns a resource. The integrity check may be valuable for risk scoring, but object-level permissions must be evaluated independently.
The device integrity reference and interactive trust lab show how multiple verification layers coexist without replacing one another.
Build test fixtures around principals and tenants
The most useful regression suite uses distinct principals with at least two tenants, objects owned by each tenant, and objects deliberately shared across a boundary. For each API action, assert the exact allowed and rejected combinations.
Positive tests alone are insufficient: a test that confirms Alice can read Alice's document will pass even if Bob can read it too.
Authorization should be checked at a consistent backend policy layer, but shared policy helpers can also be bypassed by new endpoints. Include route-level coverage and tests for administrative or support flows, not just the mobile app's most common path.
For legitimate security review, keep these tests within a system you own or are explicitly authorized to test. Use synthetic test accounts and fixtures rather than real customer objects.
What to log
Record the authenticated principal ID or an appropriate pseudonymous reference, action, resource class, authorization outcome, policy version, and a correlation ID. Avoid capturing raw bearer tokens or entire private resource bodies. An authorization denial should be observable without becoming another source of data leakage.
A 403 response is helpful only when the decision is genuinely enforced and no protected side effect happened before the check.
Editorial note: Educational API review guidance prepared with AI assistance, based on the OWASP API Security Top 10. It is not a finding against a particular production service.
Continue exploring
Related technical notes with overlapping topics.