Skip to main content
ZombieMutex

30 results · ↑ ↓ navigate · Enter open · Esc close

All writing
November 6, 20263 min read

Object-level API authorization: why a valid JWT is not enough

Practical tests for broken object-level authorization in authenticated mobile and backend APIs.

By

Share

Send this article to someone or copy its link.

Suggest a correction

Reader tools

3 min remaining · 0% of article

Select a passage and choose Highlight selection to save it. Local browser storage only. Shift+F toggles focus.

One of the first things I check when reviewing an authenticated API is whether a valid session can read or change an object owned by another account. A correctly signed JWT says who the caller is. It does not say that the caller may access a particular address, document or order.

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.

text
GET /api/v1/addresses/addr_9d8a

Authorization: Bearer <valid session token>

Server must independently verify:
  session -> principal
  resource -> access policy
  principal + action + resource -> allow or deny

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.

VariationWhat to validate
Read someone else's objectNo unauthorized data disclosed
Update another tenant's objectServer rejects before mutation
Delete or export another tenant's objectSame authorization enforcement as read/update
Change organization or workspace IDTenant boundary validated on the server
Supply an ID in request body rather than pathAuthorization still applies
Replay a cached request after role removalCurrent 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 explains why device evidence, authentication, replay resistance, and object-level authorization must be enforced independently.

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.


I prepared this API review guide with AI assistance and OWASP guidance. It isn't a finding against any particular service.

Related technical notes with overlapping topics.

Discuss a passage

Select a paragraph above, copy it with its article link, then paste into the discussion. Don't include private data.

Jump to discussion ↓

Public conversation / GitHub Discussions

Discuss this article

Read and reply using GitHub Discussions. A GitHub account is needed to post. Comments are public and subject to GitHub moderation.

For privacy, the external giscus widget does not load until you choose to open it. Opening it connects your browser to giscus and GitHub. Do not post credentials or private project data.